Back to index

lightning-sunbird  0.9+nobinonly
nsXULPrototypeCache.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 Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Chris Waterson <waterson@netscape.com>
00024  *   Brendan Eich <brendan@mozilla.org>
00025  *   Ben Goodger <ben@netscape.com>
00026  *   Benjamin Smedberg <bsmedberg@covad.net>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsCOMPtr.h"
00043 #include "nsXPIDLString.h"
00044 #include "nsContentUtils.h"
00045 #include "nsICSSStyleSheet.h"
00046 #include "nsIXULPrototypeCache.h"
00047 #include "nsIXULPrototypeDocument.h"
00048 #include "nsIXULDocument.h"
00049 #include "nsIURI.h"
00050 #include "nsIURL.h"
00051 #include "nsXPIDLString.h"
00052 #include "plstr.h"
00053 #include "nsIDocument.h"
00054 #include "nsIXBLDocumentInfo.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsXULDocument.h"
00057 #include "nsIJSRuntimeService.h"
00058 #include "jsapi.h"
00059 
00060 #include "nsIChromeRegistry.h"
00061 #include "nsIFastLoadService.h"
00062 #include "nsIFastLoadFileControl.h"
00063 #include "nsIFile.h"
00064 #include "nsIObjectInputStream.h"
00065 #include "nsIObjectOutputStream.h"
00066 #include "nsIObserver.h"
00067 #include "nsIObserverService.h"
00068 
00069 #include "nsNetUtil.h"
00070 #include "nsURIHashKey.h"
00071 #include "nsInterfaceHashtable.h"
00072 #include "nsDataHashtable.h"
00073 #include "nsAppDirectoryServiceDefs.h"
00074 
00075 #include "jsxdrapi.h"
00076 
00077 class nsXULPrototypeCache : public nsIXULPrototypeCache,
00078                                    nsIObserver
00079 {
00080 public:
00081     // nsISupports
00082     NS_DECL_ISUPPORTS
00083     NS_DECL_NSIOBSERVER
00084 
00085     NS_IMETHOD GetPrototype(nsIURI* aURI, nsIXULPrototypeDocument** _result);
00086     NS_IMETHOD PutPrototype(nsIXULPrototypeDocument* aDocument);
00087     NS_IMETHOD FlushPrototypes();
00088 
00089     NS_IMETHOD GetStyleSheet(nsIURI* aURI, nsICSSStyleSheet** _result);
00090     NS_IMETHOD PutStyleSheet(nsICSSStyleSheet* aStyleSheet);
00091     NS_IMETHOD FlushStyleSheets();
00092 
00093     NS_IMETHOD GetScript(nsIURI* aURI, void** aScriptObject);
00094     NS_IMETHOD PutScript(nsIURI* aURI, void* aScriptObject);
00095     NS_IMETHOD FlushScripts();
00096 
00097     NS_IMETHOD GetXBLDocumentInfo(nsIURI* aURL, nsIXBLDocumentInfo** _result);
00098     NS_IMETHOD PutXBLDocumentInfo(nsIXBLDocumentInfo* aDocumentInfo);
00099     NS_IMETHOD FlushXBLInformation();
00100 
00101     NS_IMETHOD Flush();
00102 
00103     NS_IMETHOD GetEnabled(PRBool* aIsEnabled);
00104 
00105     NS_IMETHOD AbortFastLoads();
00106     NS_IMETHOD GetFastLoadService(nsIFastLoadService** aResult);
00107     NS_IMETHOD RemoveFromFastLoadSet(nsIURI* aDocumentURI);
00108     NS_IMETHOD WritePrototype(nsIXULPrototypeDocument* aPrototypeDocument);
00109 
00110 protected:
00111     friend NS_IMETHODIMP
00112     NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult);
00113 
00114     nsXULPrototypeCache();
00115     virtual ~nsXULPrototypeCache();
00116 
00117     void FlushSkinFiles();
00118 
00119     JSRuntime*  GetJSRuntime();
00120 
00121     nsInterfaceHashtable<nsURIHashKey,nsIXULPrototypeDocument> mPrototypeTable;
00122     nsInterfaceHashtable<nsURIHashKey,nsICSSStyleSheet>        mStyleSheetTable;
00123     nsDataHashtable<nsURIHashKey,void*>                        mScriptTable;
00124     nsInterfaceHashtable<nsURIHashKey,nsIXBLDocumentInfo>      mXBLDocTable;
00125 
00126     JSRuntime*          mJSRuntime;
00127 
00129     // FastLoad
00130     // this is really a hash set, with a dummy data parameter
00131     nsDataHashtable<nsURIHashKey,PRUint32> mFastLoadURITable;
00132 
00133     static nsIFastLoadService*    gFastLoadService;
00134     static nsIFile*               gFastLoadFile;
00135 
00136     // Bootstrap FastLoad Service
00137     nsresult StartFastLoad(nsIURI* aDocumentURI);
00138     nsresult StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags);
00139 };
00140 
00141 static PRBool gDisableXULCache = PR_FALSE; // enabled by default
00142 static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache";
00143 
00144 //----------------------------------------------------------------------
00145 
00146 PR_STATIC_CALLBACK(int)
00147 DisableXULCacheChangedCallback(const char* aPref, void* aClosure)
00148 {
00149     gDisableXULCache =
00150         nsContentUtils::GetBoolPref(kDisableXULCachePref, gDisableXULCache);
00151 
00152     // Flush the cache, regardless
00153     static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
00154     nsCOMPtr<nsIXULPrototypeCache> cache(do_GetService(kXULPrototypeCacheCID));
00155 
00156     if (cache)
00157         cache->Flush();
00158 
00159     return 0;
00160 }
00161 
00162 //----------------------------------------------------------------------
00163 
00164 
00165 nsIFastLoadService*   nsXULPrototypeCache::gFastLoadService = nsnull;
00166 nsIFile*              nsXULPrototypeCache::gFastLoadFile = nsnull;
00167 
00168 nsXULPrototypeCache::nsXULPrototypeCache()
00169     : mJSRuntime(nsnull)
00170 {
00171 }
00172 
00173 
00174 nsXULPrototypeCache::~nsXULPrototypeCache()
00175 {
00176     FlushScripts();
00177 
00178     NS_IF_RELEASE(gFastLoadService); // don't need ReleaseService nowadays!
00179     NS_IF_RELEASE(gFastLoadFile);
00180 }
00181 
00182 
00183 NS_IMPL_THREADSAFE_ISUPPORTS2(nsXULPrototypeCache,
00184                               nsIXULPrototypeCache,
00185                               nsIObserver)
00186 
00187 
00188 NS_IMETHODIMP
00189 NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult)
00190 {
00191     NS_PRECONDITION(! aOuter, "no aggregation");
00192     if (aOuter)
00193         return NS_ERROR_NO_AGGREGATION;
00194 
00195     nsRefPtr<nsXULPrototypeCache> result = new nsXULPrototypeCache();
00196     if (! result)
00197         return NS_ERROR_OUT_OF_MEMORY;
00198 
00199     if (!(result->mPrototypeTable.Init() &&
00200           result->mStyleSheetTable.Init() &&
00201           result->mScriptTable.Init() &&
00202           result->mXBLDocTable.Init() &&
00203           result->mFastLoadURITable.Init())) {
00204         return NS_ERROR_OUT_OF_MEMORY;
00205     }
00206 
00207     // XXX Ignore return values.
00208     gDisableXULCache =
00209         nsContentUtils::GetBoolPref(kDisableXULCachePref, gDisableXULCache);
00210     nsContentUtils::RegisterPrefCallback(kDisableXULCachePref,
00211                                          DisableXULCacheChangedCallback,
00212                                          nsnull);
00213 
00214     nsresult rv = result->QueryInterface(aIID, aResult);
00215 
00216     nsCOMPtr<nsIObserverService> obsSvc(do_GetService("@mozilla.org/observer-service;1"));
00217     if (obsSvc && NS_SUCCEEDED(rv)) {
00218         nsXULPrototypeCache *p = result;
00219         obsSvc->AddObserver(p, "chrome-flush-skin-caches", PR_FALSE);
00220         obsSvc->AddObserver(p, "chrome-flush-caches", PR_FALSE);
00221     }
00222 
00223     return rv;
00224 }
00225 
00226 
00227 //----------------------------------------------------------------------
00228 
00229 NS_IMETHODIMP
00230 nsXULPrototypeCache::Observe(nsISupports* aSubject,
00231                              const char *aTopic,
00232                              const PRUnichar *aData)
00233 {
00234     if (!strcmp(aTopic, "chrome-flush-skin-caches")) {
00235         FlushSkinFiles();
00236     }
00237     else if (!strcmp(aTopic, "chrome-flush-caches")) {
00238         Flush();
00239     }
00240     else {
00241         NS_WARNING("Unexpected observer topic.");
00242     }
00243     return NS_OK;
00244 }
00245 
00246 
00247 NS_IMETHODIMP
00248 nsXULPrototypeCache::GetPrototype(nsIURI* aURI, nsIXULPrototypeDocument** _result)
00249 {
00250     nsresult rv = NS_OK;
00251 
00252     mPrototypeTable.Get(aURI, _result);
00253 
00254     if (! *_result) {
00255         // No prototype in XUL memory cache. Spin up FastLoad Service and
00256         // look in FastLoad file.
00257         rv = StartFastLoad(aURI);
00258         if (NS_SUCCEEDED(rv)) {
00259             nsCOMPtr<nsIObjectInputStream> objectInput;
00260             gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
00261 
00262             rv = StartFastLoadingURI(aURI, nsIFastLoadService::NS_FASTLOAD_READ);
00263             if (NS_SUCCEEDED(rv)) {
00264                 nsCOMPtr<nsIURI> oldURI;
00265                 gFastLoadService->SelectMuxedDocument(aURI, getter_AddRefs(oldURI));
00266 
00267                 // Create a new prototype document.
00268                 nsCOMPtr<nsIXULPrototypeDocument> protoDoc;
00269                 rv = NS_NewXULPrototypeDocument(nsnull,
00270                                                 NS_GET_IID(nsIXULPrototypeDocument),
00271                                                 getter_AddRefs(protoDoc));
00272                 if (NS_FAILED(rv)) return rv;
00273 
00274                 rv = protoDoc->Read(objectInput);
00275                 if (NS_SUCCEEDED(rv)) {
00276                     NS_ADDREF(*_result = protoDoc);
00277                     PutPrototype(protoDoc);
00278 
00279                     gFastLoadService->EndMuxedDocument(aURI);
00280                 }
00281 
00282                 RemoveFromFastLoadSet(aURI);
00283             }
00284         }
00285     }
00286 
00287     return rv;
00288 }
00289 
00290 NS_IMETHODIMP
00291 nsXULPrototypeCache::PutPrototype(nsIXULPrototypeDocument* aDocument)
00292 {
00293     nsresult rv;
00294     nsCOMPtr<nsIURI> uri;
00295     rv = aDocument->GetURI(getter_AddRefs(uri));
00296 
00297     // Put() releases any old value
00298     mPrototypeTable.Put(uri, aDocument);
00299 
00300     return NS_OK;
00301 }
00302 
00303 JSRuntime*
00304 nsXULPrototypeCache::GetJSRuntime()
00305 {
00306     if (!mJSRuntime) {
00307         nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
00308         if (rtsvc)
00309             rtsvc->GetRuntime(&mJSRuntime);
00310     }
00311 
00312     return mJSRuntime;
00313 }
00314 
00315 NS_IMETHODIMP
00316 nsXULPrototypeCache::FlushPrototypes()
00317 {
00318     mPrototypeTable.Clear();
00319 
00320     // Clear the script cache, as it refers to prototype-owned mJSObjects.
00321     FlushScripts();
00322     return NS_OK;
00323 }
00324 
00325 
00326 NS_IMETHODIMP
00327 nsXULPrototypeCache::GetStyleSheet(nsIURI* aURI, nsICSSStyleSheet** _result)
00328 {
00329     mStyleSheetTable.Get(aURI, _result);
00330     return NS_OK;
00331 }
00332 
00333 
00334 NS_IMETHODIMP
00335 nsXULPrototypeCache::PutStyleSheet(nsICSSStyleSheet* aStyleSheet)
00336 {
00337     nsresult rv;
00338     nsCOMPtr<nsIURI> uri;
00339     rv = aStyleSheet->GetSheetURI(getter_AddRefs(uri));
00340     if (NS_SUCCEEDED(rv))
00341         mStyleSheetTable.Put(uri, aStyleSheet);
00342 
00343     return rv;
00344 }
00345 
00346 
00347 NS_IMETHODIMP
00348 nsXULPrototypeCache::FlushStyleSheets()
00349 {
00350     mStyleSheetTable.Clear();
00351     return NS_OK;
00352 }
00353 
00354 
00355 NS_IMETHODIMP
00356 nsXULPrototypeCache::GetScript(nsIURI* aURI, void** aScriptObject)
00357 {
00358     if (!mScriptTable.Get(aURI, aScriptObject))
00359         *aScriptObject = nsnull;
00360 
00361     return NS_OK;
00362 }
00363 
00364 
00365 NS_IMETHODIMP
00366 nsXULPrototypeCache::PutScript(nsIURI* aURI, void* aScriptObject)
00367 {
00368     NS_ENSURE_TRUE(mScriptTable.Put(aURI, aScriptObject), NS_ERROR_OUT_OF_MEMORY);
00369 
00370     // Lock the object from being gc'd until it is removed from the cache
00371     JS_LockGCThingRT(GetJSRuntime(), aScriptObject);
00372     return NS_OK;
00373 }
00374 
00375 /* static */
00376 PR_STATIC_CALLBACK(PLDHashOperator)
00377 ReleaseJSObjectCallback(nsIURI* aKey, void* &aData, void* aClosure)
00378 {
00379     JS_UnlockGCThingRT((JSRuntime*) aClosure, aData);
00380     return PL_DHASH_REMOVE;
00381 }
00382 
00383 NS_IMETHODIMP
00384 nsXULPrototypeCache::FlushScripts()
00385 {
00386     // This callback will unlock each object so it can once again be gc'd.
00387     mScriptTable.Enumerate(ReleaseJSObjectCallback, (void*) GetJSRuntime());
00388     return NS_OK;
00389 }
00390 
00391 
00392 NS_IMETHODIMP
00393 nsXULPrototypeCache::GetXBLDocumentInfo(nsIURI* aURL, nsIXBLDocumentInfo** aResult)
00394 {
00395     mXBLDocTable.Get(aURL, aResult);
00396     return NS_OK;
00397 }
00398 
00399 
00400 NS_IMETHODIMP
00401 nsXULPrototypeCache::PutXBLDocumentInfo(nsIXBLDocumentInfo* aDocumentInfo)
00402 {
00403     nsIURI* uri = aDocumentInfo->DocumentURI();
00404 
00405     nsCOMPtr<nsIXBLDocumentInfo> info;
00406     mXBLDocTable.Get(uri, getter_AddRefs(info));
00407     if (!info)
00408         mXBLDocTable.Put(uri, aDocumentInfo);
00409 
00410     return NS_OK;
00411 }
00412 
00413 
00414 NS_IMETHODIMP
00415 nsXULPrototypeCache::FlushXBLInformation()
00416 {
00417     mXBLDocTable.Clear();
00418     return NS_OK;
00419 }
00420 
00421 PR_STATIC_CALLBACK(PLDHashOperator)
00422 FlushSkinXBL(nsIURI* aKey, nsCOMPtr<nsIXBLDocumentInfo>& aDocInfo, void* aClosure)
00423 {
00424   nsCAutoString str;
00425   aKey->GetPath(str);
00426 
00427   PLDHashOperator ret = PL_DHASH_NEXT;
00428 
00429   if (!strncmp(str.get(), "/skin", 5)) {
00430     ret = PL_DHASH_REMOVE;
00431   }
00432 
00433   return ret;
00434 }
00435 
00436 PR_STATIC_CALLBACK(PLDHashOperator)
00437 FlushSkinSheets(nsIURI* aKey, nsCOMPtr<nsICSSStyleSheet>& aSheet, void* aClosure)
00438 {
00439   nsCOMPtr<nsIURI> uri;
00440   aSheet->GetSheetURI(getter_AddRefs(uri));
00441   nsCAutoString str;
00442   uri->GetPath(str);
00443 
00444   PLDHashOperator ret = PL_DHASH_NEXT;
00445 
00446   if (!strncmp(str.get(), "/skin", 5)) {
00447     // This is a skin binding. Add the key to the list.
00448     ret = PL_DHASH_REMOVE;
00449   }
00450   return ret;
00451 }
00452 
00453 PR_STATIC_CALLBACK(PLDHashOperator)
00454 FlushScopedSkinStylesheets(nsIURI* aKey, nsCOMPtr<nsIXBLDocumentInfo> &aDocInfo, void* aClosure)
00455 {
00456   aDocInfo->FlushSkinStylesheets();
00457   return PL_DHASH_NEXT;
00458 }
00459 
00460 void
00461 nsXULPrototypeCache::FlushSkinFiles()
00462 {
00463   // Flush out skin XBL files from the cache.
00464   mXBLDocTable.Enumerate(FlushSkinXBL, nsnull);
00465 
00466   // Now flush out our skin stylesheets from the cache.
00467   mStyleSheetTable.Enumerate(FlushSkinSheets, nsnull);
00468 
00469   // Iterate over all the remaining XBL and make sure cached
00470   // scoped skin stylesheets are flushed and refetched by the
00471   // prototype bindings.
00472   mXBLDocTable.Enumerate(FlushScopedSkinStylesheets, nsnull);
00473 }
00474 
00475 
00476 NS_IMETHODIMP
00477 nsXULPrototypeCache::Flush()
00478 {
00479     FlushPrototypes();  // flushes the script table as well
00480     FlushStyleSheets();
00481     FlushXBLInformation();
00482     return NS_OK;
00483 }
00484 
00485 
00486 NS_IMETHODIMP
00487 nsXULPrototypeCache::GetEnabled(PRBool* aIsEnabled)
00488 {
00489     *aIsEnabled = !gDisableXULCache;
00490     return NS_OK;
00491 }
00492 
00493 
00494 NS_IMETHODIMP
00495 nsXULPrototypeCache::GetFastLoadService(nsIFastLoadService** aResult)
00496 {
00497     NS_IF_ADDREF(*aResult = gFastLoadService);
00498     return NS_OK;
00499 }
00500 
00501 
00502 static PRBool gDisableXULFastLoad = PR_FALSE;           // enabled by default
00503 static PRBool gChecksumXULFastLoadFile = PR_TRUE;       // XXXbe too paranoid
00504 
00505 NS_IMETHODIMP
00506 nsXULPrototypeCache::AbortFastLoads()
00507 {
00508 #ifdef DEBUG_brendan
00509     NS_BREAK();
00510 #endif
00511 
00512     // Save a strong ref to the FastLoad file, so we can remove it after we
00513     // close open streams to it.
00514     nsCOMPtr<nsIFile> file = gFastLoadFile;
00515 
00516     // Flush the XUL cache for good measure, in case we cached a bogus/downrev
00517     // script, somehow.
00518     Flush();
00519 
00520     // Clear the FastLoad set
00521     mFastLoadURITable.Clear();
00522 
00523     if (! gFastLoadService)
00524         return NS_OK;
00525 
00526     // Fetch the current input (if FastLoad file existed) or output (if we're
00527     // creating the FastLoad file during this app startup) stream.
00528     nsCOMPtr<nsIObjectInputStream> objectInput;
00529     nsCOMPtr<nsIObjectOutputStream> objectOutput;
00530     gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
00531     gFastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
00532 
00533     if (objectOutput) {
00534         gFastLoadService->SetOutputStream(nsnull);
00535 
00536         if (NS_SUCCEEDED(objectOutput->Close()) && gChecksumXULFastLoadFile)
00537             gFastLoadService->CacheChecksum(gFastLoadFile,
00538                                             objectOutput);
00539     }
00540 
00541     if (objectInput) {
00542         // If this is the last of one or more XUL master documents loaded
00543         // together at app startup, close the FastLoad service's singleton
00544         // input stream now.
00545         gFastLoadService->SetInputStream(nsnull);
00546         objectInput->Close();
00547     }
00548 
00549     // Now rename or remove the file.
00550     if (file) {
00551 #ifdef DEBUG
00552         // Remove any existing Aborted.mfasl files generated in previous runs.
00553         nsCOMPtr<nsIFile> existingAbortedFile;
00554         file->Clone(getter_AddRefs(existingAbortedFile));
00555         if (existingAbortedFile) {
00556             existingAbortedFile->SetLeafName(NS_LITERAL_STRING("Aborted.mfasl"));
00557             PRBool fileExists = PR_FALSE;
00558             existingAbortedFile->Exists(&fileExists);
00559             if (fileExists)
00560                 existingAbortedFile->Remove(PR_FALSE);
00561         }
00562         file->MoveToNative(nsnull, NS_LITERAL_CSTRING("Aborted.mfasl"));
00563 #else
00564         file->Remove(PR_FALSE);
00565 #endif
00566     }
00567 
00568     // If the list is empty now, the FastLoad process is done.
00569     NS_RELEASE(gFastLoadService);
00570     NS_RELEASE(gFastLoadFile);
00571 
00572     return NS_OK;
00573 }
00574 
00575 
00576 NS_IMETHODIMP
00577 nsXULPrototypeCache::RemoveFromFastLoadSet(nsIURI* aURI)
00578 {
00579     mFastLoadURITable.Remove(aURI);
00580     return NS_OK;
00581 }
00582 
00583 static const char kDisableXULFastLoadPref[] = "nglayout.debug.disable_xul_fastload";
00584 static const char kChecksumXULFastLoadFilePref[] = "nglayout.debug.checksum_xul_fastload_file";
00585 
00586 NS_IMETHODIMP
00587 nsXULPrototypeCache::WritePrototype(nsIXULPrototypeDocument* aPrototypeDocument)
00588 {
00589     nsresult rv = NS_OK, rv2 = NS_OK;
00590 
00591     // We're here before the FastLoad service has been initialized, probably because
00592     // of the profile manager. Bail quietly, don't worry, we'll be back later.
00593     if (! gFastLoadService)
00594         return NS_OK;
00595 
00596     // Fetch the current input (if FastLoad file existed) or output (if we're
00597     // creating the FastLoad file during this app startup) stream.
00598     nsCOMPtr<nsIObjectInputStream> objectInput;
00599     nsCOMPtr<nsIObjectOutputStream> objectOutput;
00600     gFastLoadService->GetInputStream(getter_AddRefs(objectInput));
00601     gFastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
00602 
00603     nsCOMPtr<nsIURI> protoURI;
00604     aPrototypeDocument->GetURI(getter_AddRefs(protoURI));
00605 
00606     // Remove this document from the FastLoad table. We use the table's
00607     // emptiness instead of a counter to decide when the FastLoad process
00608     // has completed. When complete, we can write footer details to the
00609     // FastLoad file.
00610     RemoveFromFastLoadSet(protoURI);
00611 
00612     PRInt32 count = mFastLoadURITable.Count();
00613 
00614     if (objectOutput) {
00615         rv = StartFastLoadingURI(protoURI, nsIFastLoadService::NS_FASTLOAD_WRITE);
00616         if (NS_SUCCEEDED(rv)) {
00617             // Re-select the URL of the current prototype, as out-of-line script loads
00618             // may have changed
00619             nsCOMPtr<nsIURI> oldURI;
00620             gFastLoadService->SelectMuxedDocument(protoURI, getter_AddRefs(oldURI));
00621 
00622             aPrototypeDocument->Write(objectOutput);
00623 
00624             gFastLoadService->EndMuxedDocument(protoURI);
00625         }
00626 
00627         // If this is the last of one or more XUL master documents loaded
00628         // together at app startup, close the FastLoad service's singleton
00629         // output stream now.
00630         //
00631         // NB: we must close input after output, in case the output stream
00632         // implementation needs to read from the input stream, to compute a
00633         // FastLoad file checksum.  In that case, the implementation used
00634         // nsIFastLoadFileIO to get the corresponding input stream for this
00635         // output stream.
00636         if (count == 0) {
00637             gFastLoadService->SetOutputStream(nsnull);
00638             rv = objectOutput->Close();
00639 
00640             if (NS_SUCCEEDED(rv) && gChecksumXULFastLoadFile) {
00641                 rv = gFastLoadService->CacheChecksum(gFastLoadFile,
00642                                                      objectOutput);
00643             }
00644         }
00645     }
00646 
00647     if (objectInput) {
00648         // If this is the last of one or more XUL master documents loaded
00649         // together at app startup, close the FastLoad service's singleton
00650         // input stream now.
00651         if (count == 0) {
00652             gFastLoadService->SetInputStream(nsnull);
00653             rv2 = objectInput->Close();
00654         }
00655     }
00656 
00657     // If the list is empty now, the FastLoad process is done.
00658     if (count == 0) {
00659         NS_RELEASE(gFastLoadService);
00660         NS_RELEASE(gFastLoadFile);
00661     }
00662 
00663     return NS_FAILED(rv) ? rv : rv2;
00664 }
00665 
00666 
00667 nsresult
00668 nsXULPrototypeCache::StartFastLoadingURI(nsIURI* aURI, PRInt32 aDirectionFlags)
00669 {
00670     nsresult rv;
00671 
00672     nsCAutoString urlspec;
00673     rv = aURI->GetAsciiSpec(urlspec);
00674     if (NS_FAILED(rv)) return rv;
00675 
00676     // If StartMuxedDocument returns NS_ERROR_NOT_AVAILABLE, then
00677     // we must be reading the file, and urlspec was not associated
00678     // with any multiplexed stream in it.  The FastLoad service
00679     // will therefore arrange to update the file, writing new data
00680     // at the end while old (available) data continues to be read
00681     // from the pre-existing part of the file.
00682     return gFastLoadService->StartMuxedDocument(aURI, urlspec.get(), aDirectionFlags);
00683 }
00684 
00685 PR_STATIC_CALLBACK(int)
00686 FastLoadPrefChangedCallback(const char* aPref, void* aClosure)
00687 {
00688     PRBool wasEnabled = !gDisableXULFastLoad;
00689     gDisableXULFastLoad =
00690         nsContentUtils::GetBoolPref(kDisableXULFastLoadPref,
00691                                     gDisableXULFastLoad);
00692 
00693     if (wasEnabled && gDisableXULFastLoad) {
00694         static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
00695         nsCOMPtr<nsIXULPrototypeCache> cache =
00696             do_GetService(kXULPrototypeCacheCID);
00697 
00698         if (cache)
00699             cache->AbortFastLoads();
00700     }
00701 
00702     gChecksumXULFastLoadFile =
00703         nsContentUtils::GetBoolPref(kChecksumXULFastLoadFilePref,
00704                                     gChecksumXULFastLoadFile);
00705 
00706     return 0;
00707 }
00708 
00709 
00710 class nsXULFastLoadFileIO : public nsIFastLoadFileIO
00711 {
00712   public:
00713     nsXULFastLoadFileIO(nsIFile* aFile)
00714       : mFile(aFile) {
00715         MOZ_COUNT_CTOR(nsXULFastLoadFileIO);
00716     }
00717 
00718     virtual ~nsXULFastLoadFileIO() {
00719         MOZ_COUNT_DTOR(nsXULFastLoadFileIO);
00720     }
00721 
00722     NS_DECL_ISUPPORTS
00723     NS_DECL_NSIFASTLOADFILEIO
00724 
00725     nsCOMPtr<nsIFile>         mFile;
00726     nsCOMPtr<nsIInputStream>  mInputStream;
00727     nsCOMPtr<nsIOutputStream> mOutputStream;
00728 };
00729 
00730 
00731 NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULFastLoadFileIO, nsIFastLoadFileIO)
00732 MOZ_DECL_CTOR_COUNTER(nsXULFastLoadFileIO)
00733 
00734 
00735 NS_IMETHODIMP
00736 nsXULFastLoadFileIO::GetInputStream(nsIInputStream** aResult)
00737 {
00738     if (! mInputStream) {
00739         nsresult rv;
00740         nsCOMPtr<nsIInputStream> fileInput;
00741         rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInput), mFile);
00742         if (NS_FAILED(rv)) return rv;
00743 
00744         rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
00745                                        fileInput,
00746                                        XUL_DESERIALIZATION_BUFFER_SIZE);
00747         if (NS_FAILED(rv)) return rv;
00748     }
00749 
00750     NS_ADDREF(*aResult = mInputStream);
00751     return NS_OK;
00752 }
00753 
00754 
00755 NS_IMETHODIMP
00756 nsXULFastLoadFileIO::GetOutputStream(nsIOutputStream** aResult)
00757 {
00758     if (! mOutputStream) {
00759         PRInt32 ioFlags = PR_WRONLY;
00760         if (! mInputStream)
00761             ioFlags |= PR_CREATE_FILE | PR_TRUNCATE;
00762 
00763         nsresult rv;
00764         nsCOMPtr<nsIOutputStream> fileOutput;
00765         rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutput), mFile,
00766                                          ioFlags, 0644);
00767         if (NS_FAILED(rv)) return rv;
00768 
00769         rv = NS_NewBufferedOutputStream(getter_AddRefs(mOutputStream),
00770                                         fileOutput,
00771                                         XUL_SERIALIZATION_BUFFER_SIZE);
00772         if (NS_FAILED(rv)) return rv;
00773     }
00774 
00775     NS_ADDREF(*aResult = mOutputStream);
00776     return NS_OK;
00777 }
00778 
00779 nsresult
00780 nsXULPrototypeCache::StartFastLoad(nsIURI* aURI)
00781 {
00782     nsresult rv;
00783 
00784     nsCAutoString path;
00785     aURI->GetPath(path);
00786     if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul")))
00787         return NS_ERROR_NOT_AVAILABLE;
00788 
00789     // Test gFastLoadFile to decide whether this is the first nsXULDocument
00790     // participating in FastLoad.  If gFastLoadFile is non-null, this document
00791     // must not be first, but it can join the FastLoad process.  Examples of
00792     // multiple master documents participating include hiddenWindow.xul and
00793     // navigator.xul on the Mac, and multiple-app-component (e.g., mailnews
00794     // and browser) startup due to command-line arguments.
00795     //
00796     // XXXbe we should attempt to update the FastLoad file after startup!
00797     //
00798     // XXXbe we do not yet use nsFastLoadPtrs, but once we do, we must keep
00799     // the FastLoad input stream open for the life of the app.
00800     if (gFastLoadService && gFastLoadFile) {
00801         mFastLoadURITable.Put(aURI, 1);
00802 
00803         return NS_OK;
00804     }
00805 
00806     // Use a local to refer to the service till we're sure we succeeded, then
00807     // commit to gFastLoadService.  Same for gFastLoadFile, which is used to
00808     // delete the FastLoad file on abort.
00809     nsCOMPtr<nsIFastLoadService> fastLoadService(do_GetFastLoadService());
00810     if (! fastLoadService)
00811         return NS_ERROR_FAILURE;
00812 
00813     gDisableXULFastLoad =
00814         nsContentUtils::GetBoolPref(kDisableXULFastLoadPref,
00815                                     gDisableXULFastLoad);
00816     gChecksumXULFastLoadFile =
00817         nsContentUtils::GetBoolPref(kChecksumXULFastLoadFilePref,
00818                                     gChecksumXULFastLoadFile);
00819     nsContentUtils::RegisterPrefCallback(kDisableXULFastLoadPref,
00820                                          FastLoadPrefChangedCallback,
00821                                          nsnull);
00822     nsContentUtils::RegisterPrefCallback(kChecksumXULFastLoadFilePref,
00823                                          FastLoadPrefChangedCallback,
00824                                          nsnull);
00825 
00826     if (gDisableXULFastLoad)
00827         return NS_ERROR_NOT_AVAILABLE;
00828 
00829     // Get the chrome directory to validate against the one stored in the
00830     // FastLoad file, or to store there if we're generating a new file.
00831     nsCOMPtr<nsIFile> chromeDir;
00832     rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir));
00833     if (NS_FAILED(rv))
00834         return rv;
00835     nsCAutoString chromePath;
00836     rv = chromeDir->GetNativePath(chromePath);
00837     if (NS_FAILED(rv))
00838         return rv;
00839 
00840     nsCOMPtr<nsIFile> file;
00841     rv = fastLoadService->NewFastLoadFile(XUL_FASTLOAD_FILE_BASENAME,
00842                                           getter_AddRefs(file));
00843     if (NS_FAILED(rv))
00844         return rv;
00845 
00846     // Give the FastLoad service an object by which it can get or create a
00847     // file output stream given an input stream on the same file.
00848     nsXULFastLoadFileIO* xio = new nsXULFastLoadFileIO(file);
00849     nsCOMPtr<nsIFastLoadFileIO> io = NS_STATIC_CAST(nsIFastLoadFileIO*, xio);
00850     if (! io)
00851         return NS_ERROR_OUT_OF_MEMORY;
00852     fastLoadService->SetFileIO(io);
00853 
00854     nsCOMPtr<nsIXULChromeRegistry> chromeReg(do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv));
00855     if (NS_FAILED(rv))
00856         return rv;
00857 
00858     // XXXbe we assume the first package's locale is the same as the locale of
00859     // all subsequent packages of FastLoaded chrome URIs....
00860     nsCAutoString package;
00861     rv = aURI->GetHost(package);
00862     if (NS_FAILED(rv))
00863         return rv;
00864 
00865     nsCAutoString locale;
00866     rv = chromeReg->GetSelectedLocale(package, locale);
00867     if (NS_FAILED(rv))
00868         return rv;
00869 
00870     // Try to read an existent FastLoad file.
00871     PRBool exists = PR_FALSE;
00872     if (NS_SUCCEEDED(file->Exists(&exists)) && exists) {
00873         nsCOMPtr<nsIInputStream> input;
00874         rv = io->GetInputStream(getter_AddRefs(input));
00875         if (NS_FAILED(rv))
00876             return rv;
00877 
00878         nsCOMPtr<nsIObjectInputStream> objectInput;
00879         rv = fastLoadService->NewInputStream(input, getter_AddRefs(objectInput));
00880 
00881         if (NS_SUCCEEDED(rv)) {
00882             if (gChecksumXULFastLoadFile) {
00883                 nsCOMPtr<nsIFastLoadReadControl>
00884                     readControl(do_QueryInterface(objectInput));
00885                 if (readControl) {
00886                     // Verify checksum, using the fastLoadService's checksum
00887                     // cache to avoid computing more than once per session.
00888                     PRUint32 checksum;
00889                     rv = readControl->GetChecksum(&checksum);
00890                     if (NS_SUCCEEDED(rv)) {
00891                         PRUint32 verified;
00892                         rv = fastLoadService->ComputeChecksum(file,
00893                                                                readControl,
00894                                                                &verified);
00895                         if (NS_SUCCEEDED(rv) && verified != checksum) {
00896 #ifdef DEBUG
00897                             printf("bad FastLoad file checksum\n");
00898 #endif
00899                             rv = NS_ERROR_FAILURE;
00900                         }
00901                     }
00902                 }
00903             }
00904 
00905             if (NS_SUCCEEDED(rv)) {
00906                 // Get the XUL fastload file version number, which should be
00907                 // decremented whenever the XUL-specific file format changes
00908                 // (see public/nsIXULPrototypeCache.h for the #define).
00909                 PRUint32 xulFastLoadVersion, jsByteCodeVersion;
00910                 rv = objectInput->Read32(&xulFastLoadVersion);
00911                 rv |= objectInput->Read32(&jsByteCodeVersion);
00912                 if (NS_SUCCEEDED(rv)) {
00913                     if (xulFastLoadVersion != XUL_FASTLOAD_FILE_VERSION ||
00914                         jsByteCodeVersion != JSXDR_BYTECODE_VERSION) {
00915 #ifdef DEBUG
00916                         printf((xulFastLoadVersion != XUL_FASTLOAD_FILE_VERSION)
00917                                ? "bad FastLoad file version\n"
00918                                : "bad JS bytecode version\n");
00919 #endif
00920                         rv = NS_ERROR_UNEXPECTED;
00921                     } else {
00922                         nsCAutoString fileChromePath, fileLocale;
00923 
00924                         rv = objectInput->ReadCString(fileChromePath);
00925                         rv |= objectInput->ReadCString(fileLocale);
00926                         if (NS_SUCCEEDED(rv) &&
00927                             (!fileChromePath.Equals(chromePath) ||
00928                              !fileLocale.Equals(locale))) {
00929                             rv = NS_ERROR_UNEXPECTED;
00930                         }
00931                     }
00932                 }
00933             }
00934         }
00935 
00936         if (NS_SUCCEEDED(rv)) {
00937             fastLoadService->SetInputStream(objectInput);
00938         } else {
00939             // NB: we must close before attempting to remove, for non-Unix OSes
00940             // that can't do open-unlink.
00941             if (objectInput)
00942                 objectInput->Close();
00943             else
00944                 input->Close();
00945             xio->mInputStream = nsnull;
00946 
00947 #ifdef DEBUG
00948             file->MoveToNative(nsnull, NS_LITERAL_CSTRING("Invalid.mfasl"));
00949 #else
00950             file->Remove(PR_FALSE);
00951 #endif
00952             exists = PR_FALSE;
00953         }
00954     }
00955 
00956     // FastLoad file not found, or invalid: write a new one.
00957     if (! exists) {
00958         nsCOMPtr<nsIOutputStream> output;
00959         rv = io->GetOutputStream(getter_AddRefs(output));
00960         if (NS_FAILED(rv))
00961             return rv;
00962 
00963         nsCOMPtr<nsIObjectOutputStream> objectOutput;
00964         rv = fastLoadService->NewOutputStream(output,
00965                                               getter_AddRefs(objectOutput));
00966         if (NS_SUCCEEDED(rv)) {
00967             rv = objectOutput->Write32(XUL_FASTLOAD_FILE_VERSION);
00968             rv |= objectOutput->Write32(JSXDR_BYTECODE_VERSION);
00969             rv |= objectOutput->WriteStringZ(chromePath.get());
00970             rv |= objectOutput->WriteStringZ(locale.get());
00971         }
00972 
00973         // Remove here even though some errors above will lead to a FastLoad
00974         // file invalidation.  Other errors (failure to note the dependency on
00975         // installed-chrome.txt, e.g.) will not cause invalidation, and we may
00976         // as well tidy up now.
00977         if (NS_FAILED(rv)) {
00978             if (objectOutput)
00979                 objectOutput->Close();
00980             else
00981                 output->Close();
00982             xio->mOutputStream = nsnull;
00983 
00984             file->Remove(PR_FALSE);
00985             return NS_ERROR_FAILURE;
00986         }
00987 
00988         fastLoadService->SetOutputStream(objectOutput);
00989     }
00990 
00991     // Success!  Insert this URI into the mFastLoadURITable
00992     // and commit locals to globals.
00993     mFastLoadURITable.Put(aURI, 1);
00994 
00995     NS_ADDREF(gFastLoadService = fastLoadService);
00996     NS_ADDREF(gFastLoadFile = file);
00997     return NS_OK;
00998 }
00999