LCOV - code coverage report
Current view: directory - content/xul/document/src - nsXULPrototypeCache.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 326 55 16.9 %
Date: 2012-06-02 Functions: 37 12 32.4 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla Communicator client code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Chris Waterson <waterson@netscape.com>
      24                 :  *   Brendan Eich <brendan@mozilla.org>
      25                 :  *   Ben Goodger <ben@netscape.com>
      26                 :  *   Benjamin Smedberg <bsmedberg@covad.net>
      27                 :  *   Mark Hammond <mhammond@skippinet.com.au>
      28                 :  *
      29                 :  * Alternatively, the contents of this file may be used under the terms of
      30                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      31                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      32                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      33                 :  * of those above. If you wish to allow use of your version of this file only
      34                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      35                 :  * use your version of this file under the terms of the MPL, indicate your
      36                 :  * decision by deleting the provisions above and replace them with the notice
      37                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      38                 :  * the provisions above, a recipient may use your version of this file under
      39                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      40                 :  *
      41                 :  * ***** END LICENSE BLOCK ***** */
      42                 : 
      43                 : #include "nsXULPrototypeCache.h"
      44                 : 
      45                 : #include "plstr.h"
      46                 : #include "nsXULPrototypeDocument.h"
      47                 : #include "nsCSSStyleSheet.h"
      48                 : #include "nsIScriptRuntime.h"
      49                 : #include "nsIServiceManager.h"
      50                 : #include "nsIURI.h"
      51                 : 
      52                 : #include "nsIChromeRegistry.h"
      53                 : #include "nsIFile.h"
      54                 : #include "nsIObjectInputStream.h"
      55                 : #include "nsIObjectOutputStream.h"
      56                 : #include "nsIObserverService.h"
      57                 : #include "nsIStringStream.h"
      58                 : #include "nsIStorageStream.h"
      59                 : 
      60                 : #include "nsNetUtil.h"
      61                 : #include "nsAppDirectoryServiceDefs.h"
      62                 : 
      63                 : #include "jsxdrapi.h"
      64                 : 
      65                 : #include "mozilla/Preferences.h"
      66                 : #include "mozilla/scache/StartupCache.h"
      67                 : #include "mozilla/scache/StartupCacheUtils.h"
      68                 : 
      69                 : using namespace mozilla;
      70                 : using namespace mozilla::scache;
      71                 : 
      72                 : static bool gDisableXULCache = false; // enabled by default
      73                 : static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache";
      74                 : static const char kXULCacheInfoKey[] = "nsXULPrototypeCache.startupCache";
      75                 : static const char kXULCachePrefix[] = "xulcache";
      76                 : 
      77                 : //----------------------------------------------------------------------
      78                 : 
      79                 : static int
      80               0 : DisableXULCacheChangedCallback(const char* aPref, void* aClosure)
      81                 : {
      82                 :     gDisableXULCache =
      83               0 :         Preferences::GetBool(kDisableXULCachePref, gDisableXULCache);
      84                 : 
      85                 :     // Flush the cache, regardless
      86               0 :     nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
      87               0 :     if (cache)
      88               0 :         cache->Flush();
      89                 : 
      90               0 :     return 0;
      91                 : }
      92                 : 
      93                 : //----------------------------------------------------------------------
      94                 : 
      95                 : StartupCache*   nsXULPrototypeCache::gStartupCache = nsnull;
      96                 : nsXULPrototypeCache*  nsXULPrototypeCache::sInstance = nsnull;
      97                 : 
      98                 : 
      99             140 : nsXULPrototypeCache::nsXULPrototypeCache()
     100                 : {
     101             140 : }
     102                 : 
     103                 : 
     104             420 : nsXULPrototypeCache::~nsXULPrototypeCache()
     105                 : {
     106             140 :     FlushScripts();
     107             560 : }
     108                 : 
     109                 : 
     110            2588 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULPrototypeCache, nsIObserver)
     111                 : 
     112                 : /* static */ nsXULPrototypeCache*
     113             330 : nsXULPrototypeCache::GetInstance()
     114                 : {
     115             330 :     if (!sInstance) {
     116             140 :         NS_ADDREF(sInstance = new nsXULPrototypeCache());
     117                 : 
     118             140 :         sInstance->mPrototypeTable.Init();
     119             140 :         sInstance->mStyleSheetTable.Init();
     120             140 :         sInstance->mScriptTable.Init();
     121             140 :         sInstance->mXBLDocTable.Init();
     122                 : 
     123             140 :         sInstance->mCacheURITable.Init();
     124             140 :         sInstance->mInputStreamTable.Init();
     125             140 :         sInstance->mOutputStreamTable.Init();
     126                 : 
     127                 :         gDisableXULCache =
     128             140 :             Preferences::GetBool(kDisableXULCachePref, gDisableXULCache);
     129                 :         Preferences::RegisterCallback(DisableXULCacheChangedCallback,
     130             140 :                                       kDisableXULCachePref);
     131                 : 
     132                 :         nsCOMPtr<nsIObserverService> obsSvc =
     133             280 :             mozilla::services::GetObserverService();
     134             140 :         if (obsSvc) {
     135             140 :             nsXULPrototypeCache *p = sInstance;
     136             140 :             obsSvc->AddObserver(p, "chrome-flush-skin-caches", false);
     137             140 :             obsSvc->AddObserver(p, "chrome-flush-caches", false);
     138             140 :             obsSvc->AddObserver(p, "startupcache-invalidate", false);
     139                 :         }
     140                 :                 
     141                 :     }
     142             330 :     return sInstance;
     143                 : }
     144                 : 
     145                 : /* static */ StartupCache*
     146               0 : nsXULPrototypeCache::GetStartupCache()
     147                 : {
     148               0 :     return gStartupCache;
     149                 : }
     150                 : 
     151                 : //----------------------------------------------------------------------
     152                 : 
     153                 : NS_IMETHODIMP
     154             367 : nsXULPrototypeCache::Observe(nsISupports* aSubject,
     155                 :                              const char *aTopic,
     156                 :                              const PRUnichar *aData)
     157                 : {
     158             367 :     if (!strcmp(aTopic, "chrome-flush-skin-caches")) {
     159             115 :         FlushSkinFiles();
     160                 :     }
     161             252 :     else if (!strcmp(aTopic, "chrome-flush-caches")) {
     162             112 :         Flush();
     163                 :     }
     164             140 :     else if (!strcmp(aTopic, "startupcache-invalidate")) {
     165             140 :         AbortCaching();
     166                 :     }
     167                 :     else {
     168               0 :         NS_WARNING("Unexpected observer topic.");
     169                 :     }
     170             367 :     return NS_OK;
     171                 : }
     172                 : 
     173                 : nsXULPrototypeDocument*
     174               0 : nsXULPrototypeCache::GetPrototype(nsIURI* aURI)
     175                 : {
     176               0 :     nsXULPrototypeDocument* protoDoc = mPrototypeTable.GetWeak(aURI);
     177               0 :     if (protoDoc)
     178               0 :         return protoDoc;
     179                 : 
     180               0 :     nsresult rv = BeginCaching(aURI);
     181               0 :     if (NS_FAILED(rv))
     182               0 :         return nsnull;
     183                 : 
     184                 :     // No prototype in XUL memory cache. Spin up the cache Service.
     185               0 :     nsCOMPtr<nsIObjectInputStream> ois;
     186               0 :     rv = GetInputStream(aURI, getter_AddRefs(ois));
     187               0 :     if (NS_FAILED(rv))
     188               0 :         return nsnull;
     189                 :     
     190               0 :     nsRefPtr<nsXULPrototypeDocument> newProto;
     191               0 :     rv = NS_NewXULPrototypeDocument(getter_AddRefs(newProto));
     192               0 :     if (NS_FAILED(rv))
     193               0 :         return nsnull;
     194                 :     
     195               0 :     rv = newProto->Read(ois);
     196               0 :     if (NS_SUCCEEDED(rv)) {
     197               0 :         rv = PutPrototype(newProto);
     198                 :     } else {
     199               0 :         newProto = nsnull;
     200                 :     }
     201                 :     
     202               0 :     mInputStreamTable.Remove(aURI);
     203               0 :     RemoveFromCacheSet(aURI);
     204               0 :     return newProto;
     205                 : }
     206                 : 
     207                 : nsresult
     208               0 : nsXULPrototypeCache::PutPrototype(nsXULPrototypeDocument* aDocument)
     209                 : {
     210               0 :     nsCOMPtr<nsIURI> uri = aDocument->GetURI();
     211                 :     // Put() releases any old value and addrefs the new one
     212               0 :     NS_ENSURE_TRUE(mPrototypeTable.Put(uri, aDocument), NS_ERROR_OUT_OF_MEMORY);
     213                 : 
     214               0 :     return NS_OK;
     215                 : }
     216                 : 
     217                 : nsresult
     218               0 : nsXULPrototypeCache::PutStyleSheet(nsCSSStyleSheet* aStyleSheet)
     219                 : {
     220               0 :     nsIURI* uri = aStyleSheet->GetSheetURI();
     221                 : 
     222               0 :     NS_ENSURE_TRUE(mStyleSheetTable.Put(uri, aStyleSheet),
     223                 :                    NS_ERROR_OUT_OF_MEMORY);
     224                 : 
     225               0 :     return NS_OK;
     226                 : }
     227                 : 
     228                 : 
     229                 : JSScript*
     230               0 : nsXULPrototypeCache::GetScript(nsIURI* aURI, PRUint32 *aLangID)
     231                 : {
     232                 :     CacheScriptEntry entry;
     233               0 :     if (!mScriptTable.Get(aURI, &entry)) {
     234               0 :         *aLangID = nsIProgrammingLanguage::UNKNOWN;
     235               0 :         return nsnull;
     236                 :     }
     237               0 :     *aLangID = entry.mScriptTypeID;
     238               0 :     return entry.mScriptObject;
     239                 : }
     240                 : 
     241                 : 
     242                 : /* static */
     243                 : static PLDHashOperator
     244               0 : ReleaseScriptObjectCallback(nsIURI* aKey, CacheScriptEntry &aData, void* aClosure)
     245                 : {
     246               0 :     nsCOMPtr<nsIScriptRuntime> rt;
     247               0 :     if (NS_SUCCEEDED(NS_GetScriptRuntimeByID(aData.mScriptTypeID, getter_AddRefs(rt))))
     248               0 :         rt->DropScriptObject(aData.mScriptObject);
     249               0 :     return PL_DHASH_REMOVE;
     250                 : }
     251                 : 
     252                 : nsresult
     253               0 : nsXULPrototypeCache::PutScript(nsIURI* aURI, PRUint32 aLangID, JSScript* aScriptObject)
     254                 : {
     255                 :     CacheScriptEntry existingEntry;
     256               0 :     if (mScriptTable.Get(aURI, &existingEntry)) {
     257               0 :         NS_WARNING("loaded the same script twice (bug 392650)");
     258                 : 
     259                 :         // Reuse the callback used for enumeration in FlushScripts
     260               0 :         ReleaseScriptObjectCallback(aURI, existingEntry, nsnull);
     261                 :     }
     262                 : 
     263               0 :     CacheScriptEntry entry = {aLangID, aScriptObject};
     264                 : 
     265               0 :     NS_ENSURE_TRUE(mScriptTable.Put(aURI, entry), NS_ERROR_OUT_OF_MEMORY);
     266                 : 
     267                 :     // Lock the object from being gc'd until it is removed from the cache
     268               0 :     nsCOMPtr<nsIScriptRuntime> rt;
     269               0 :     nsresult rv = NS_GetScriptRuntimeByID(aLangID, getter_AddRefs(rt));
     270               0 :     if (NS_SUCCEEDED(rv))
     271               0 :         rv = rt->HoldScriptObject(aScriptObject);
     272               0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to GC lock the object");
     273                 : 
     274                 :     // On failure doing the lock, we should remove the map entry?
     275               0 :     return rv;
     276                 : }
     277                 : 
     278                 : void
     279             442 : nsXULPrototypeCache::FlushScripts()
     280                 : {
     281                 :     // This callback will unlock each object so it can once again be gc'd.
     282                 :     // XXX - this might be slow - we fetch the runtime each and every object.
     283             442 :     mScriptTable.Enumerate(ReleaseScriptObjectCallback, nsnull);
     284             442 : }
     285                 : 
     286                 : 
     287                 : nsresult
     288               0 : nsXULPrototypeCache::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
     289                 : {
     290               0 :     nsIURI* uri = aDocumentInfo->DocumentURI();
     291                 : 
     292               0 :     nsRefPtr<nsXBLDocumentInfo> info;
     293               0 :     mXBLDocTable.Get(uri, getter_AddRefs(info));
     294               0 :     if (!info) {
     295               0 :         NS_ENSURE_TRUE(mXBLDocTable.Put(uri, aDocumentInfo),
     296                 :                        NS_ERROR_OUT_OF_MEMORY);
     297                 :     }
     298               0 :     return NS_OK;
     299                 : }
     300                 : 
     301                 : static PLDHashOperator
     302               0 : FlushSkinXBL(nsIURI* aKey, nsRefPtr<nsXBLDocumentInfo>& aDocInfo, void* aClosure)
     303                 : {
     304               0 :   nsCAutoString str;
     305               0 :   aKey->GetPath(str);
     306                 : 
     307               0 :   PLDHashOperator ret = PL_DHASH_NEXT;
     308                 : 
     309               0 :   if (!strncmp(str.get(), "/skin", 5)) {
     310               0 :     ret = PL_DHASH_REMOVE;
     311                 :   }
     312                 : 
     313               0 :   return ret;
     314                 : }
     315                 : 
     316                 : static PLDHashOperator
     317               0 : FlushSkinSheets(nsIURI* aKey, nsRefPtr<nsCSSStyleSheet>& aSheet, void* aClosure)
     318                 : {
     319               0 :   nsCAutoString str;
     320               0 :   aSheet->GetSheetURI()->GetPath(str);
     321                 : 
     322               0 :   PLDHashOperator ret = PL_DHASH_NEXT;
     323                 : 
     324               0 :   if (!strncmp(str.get(), "/skin", 5)) {
     325                 :     // This is a skin binding. Add the key to the list.
     326               0 :     ret = PL_DHASH_REMOVE;
     327                 :   }
     328               0 :   return ret;
     329                 : }
     330                 : 
     331                 : static PLDHashOperator
     332               0 : FlushScopedSkinStylesheets(nsIURI* aKey, nsRefPtr<nsXBLDocumentInfo> &aDocInfo, void* aClosure)
     333                 : {
     334               0 :   aDocInfo->FlushSkinStylesheets();
     335               0 :   return PL_DHASH_NEXT;
     336                 : }
     337                 : 
     338                 : void
     339             115 : nsXULPrototypeCache::FlushSkinFiles()
     340                 : {
     341                 :   // Flush out skin XBL files from the cache.
     342             115 :   mXBLDocTable.Enumerate(FlushSkinXBL, nsnull);
     343                 : 
     344                 :   // Now flush out our skin stylesheets from the cache.
     345             115 :   mStyleSheetTable.Enumerate(FlushSkinSheets, nsnull);
     346                 : 
     347                 :   // Iterate over all the remaining XBL and make sure cached
     348                 :   // scoped skin stylesheets are flushed and refetched by the
     349                 :   // prototype bindings.
     350             115 :   mXBLDocTable.Enumerate(FlushScopedSkinStylesheets, nsnull);
     351             115 : }
     352                 : 
     353                 : 
     354                 : void
     355             302 : nsXULPrototypeCache::Flush()
     356                 : {
     357             302 :     mPrototypeTable.Clear();
     358                 : 
     359                 :     // Clear the script cache, as it refers to prototype-owned mJSObjects.
     360             302 :     FlushScripts();
     361                 : 
     362             302 :     mStyleSheetTable.Clear();
     363             302 :     mXBLDocTable.Clear();
     364             302 : }
     365                 : 
     366                 : 
     367                 : bool
     368               0 : nsXULPrototypeCache::IsEnabled()
     369                 : {
     370               0 :     return !gDisableXULCache;
     371                 : }
     372                 : 
     373                 : static bool gDisableXULDiskCache = false;           // enabled by default
     374                 : 
     375                 : void
     376             140 : nsXULPrototypeCache::AbortCaching()
     377                 : {
     378                 : #ifdef DEBUG_brendan
     379                 :     NS_BREAK();
     380                 : #endif
     381                 : 
     382                 :     // Flush the XUL cache for good measure, in case we cached a bogus/downrev
     383                 :     // script, somehow.
     384             140 :     Flush();
     385                 : 
     386                 :     // Clear the cache set
     387             140 :     mCacheURITable.Clear();
     388             140 : }
     389                 : 
     390                 : 
     391                 : static const char kDisableXULDiskCachePref[] = "nglayout.debug.disable_xul_fastload";
     392                 : 
     393                 : void
     394               0 : nsXULPrototypeCache::RemoveFromCacheSet(nsIURI* aURI)
     395                 : {
     396               0 :     mCacheURITable.Remove(aURI);
     397               0 : }
     398                 : 
     399                 : nsresult
     400               0 : nsXULPrototypeCache::WritePrototype(nsXULPrototypeDocument* aPrototypeDocument)
     401                 : {
     402               0 :     nsresult rv = NS_OK, rv2 = NS_OK;
     403                 : 
     404                 :     // We're here before the startupcache service has been initialized, probably because
     405                 :     // of the profile manager. Bail quietly, don't worry, we'll be back later.
     406               0 :     if (!gStartupCache)
     407               0 :         return NS_OK;
     408                 : 
     409               0 :     nsCOMPtr<nsIURI> protoURI = aPrototypeDocument->GetURI();
     410                 : 
     411                 :     // Remove this document from the cache table. We use the table's
     412                 :     // emptiness instead of a counter to decide when the caching process 
     413                 :     // has completed.
     414               0 :     RemoveFromCacheSet(protoURI);
     415                 : 
     416               0 :     nsCOMPtr<nsIObjectOutputStream> oos;
     417               0 :     rv = GetOutputStream(protoURI, getter_AddRefs(oos));
     418               0 :     NS_ENSURE_SUCCESS(rv, rv);
     419                 : 
     420               0 :     rv = aPrototypeDocument->Write(oos);
     421               0 :     NS_ENSURE_SUCCESS(rv, rv);
     422               0 :     FinishOutputStream(protoURI);
     423               0 :     return NS_FAILED(rv) ? rv : rv2;
     424                 : }
     425                 : 
     426                 : nsresult
     427               0 : nsXULPrototypeCache::GetInputStream(nsIURI* uri, nsIObjectInputStream** stream) 
     428                 : {
     429               0 :     nsCAutoString spec(kXULCachePrefix);
     430               0 :     nsresult rv = PathifyURI(uri, spec);
     431               0 :     if (NS_FAILED(rv)) 
     432               0 :         return NS_ERROR_NOT_AVAILABLE;
     433                 :     
     434               0 :     nsAutoArrayPtr<char> buf;
     435                 :     PRUint32 len;
     436               0 :     nsCOMPtr<nsIObjectInputStream> ois;
     437               0 :     if (!gStartupCache)
     438               0 :         return NS_ERROR_NOT_AVAILABLE;
     439                 :     
     440               0 :     rv = gStartupCache->GetBuffer(spec.get(), getter_Transfers(buf), &len);
     441               0 :     if (NS_FAILED(rv)) 
     442               0 :         return NS_ERROR_NOT_AVAILABLE;
     443                 : 
     444               0 :     rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
     445               0 :     NS_ENSURE_SUCCESS(rv, rv);
     446               0 :     buf.forget();
     447                 : 
     448               0 :     mInputStreamTable.Put(uri, ois);
     449                 :     
     450               0 :     NS_ADDREF(*stream = ois);
     451               0 :     return NS_OK;
     452                 : }
     453                 : 
     454                 : nsresult
     455               0 : nsXULPrototypeCache::FinishInputStream(nsIURI* uri) {
     456               0 :     mInputStreamTable.Remove(uri);
     457               0 :     return NS_OK;
     458                 : }
     459                 : 
     460                 : nsresult
     461               0 : nsXULPrototypeCache::GetOutputStream(nsIURI* uri, nsIObjectOutputStream** stream)
     462                 : {
     463                 :     nsresult rv;
     464               0 :     nsCOMPtr<nsIObjectOutputStream> objectOutput;
     465               0 :     nsCOMPtr<nsIStorageStream> storageStream;
     466               0 :     bool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream));
     467               0 :     if (found) {
     468               0 :         objectOutput = do_CreateInstance("mozilla.org/binaryoutputstream;1");
     469               0 :         if (!objectOutput) return NS_ERROR_OUT_OF_MEMORY;
     470                 :         nsCOMPtr<nsIOutputStream> outputStream
     471               0 :             = do_QueryInterface(storageStream);
     472               0 :         objectOutput->SetOutputStream(outputStream);
     473                 :     } else {
     474               0 :         rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput), 
     475               0 :                                                  getter_AddRefs(storageStream),
     476               0 :                                                  false);
     477               0 :         NS_ENSURE_SUCCESS(rv, rv);
     478               0 :         mOutputStreamTable.Put(uri, storageStream);
     479                 :     }
     480               0 :     NS_ADDREF(*stream = objectOutput);
     481               0 :     return NS_OK;
     482                 : }
     483                 : 
     484                 : nsresult
     485               0 : nsXULPrototypeCache::FinishOutputStream(nsIURI* uri) 
     486                 : {
     487                 :     nsresult rv;
     488               0 :     if (!gStartupCache)
     489               0 :         return NS_ERROR_NOT_AVAILABLE;
     490                 :     
     491               0 :     nsCOMPtr<nsIStorageStream> storageStream;
     492               0 :     bool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream));
     493               0 :     if (!found)
     494               0 :         return NS_ERROR_UNEXPECTED;
     495                 :     nsCOMPtr<nsIOutputStream> outputStream
     496               0 :         = do_QueryInterface(storageStream);
     497               0 :     outputStream->Close();
     498                 :     
     499               0 :     nsAutoArrayPtr<char> buf;
     500                 :     PRUint32 len;
     501                 :     rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), 
     502               0 :                                     &len);
     503               0 :     NS_ENSURE_SUCCESS(rv, rv);
     504                 : 
     505               0 :     nsCAutoString spec(kXULCachePrefix);
     506               0 :     rv = PathifyURI(uri, spec);
     507               0 :     if (NS_FAILED(rv))
     508               0 :         return NS_ERROR_NOT_AVAILABLE;
     509               0 :     rv = gStartupCache->PutBuffer(spec.get(), buf, len);
     510               0 :     if (NS_SUCCEEDED(rv))
     511               0 :         mOutputStreamTable.Remove(uri);
     512                 :     
     513               0 :     return rv;
     514                 : }
     515                 : 
     516                 : // We have data if we're in the middle of writing it or we already
     517                 : // have it in the cache.
     518                 : nsresult
     519               0 : nsXULPrototypeCache::HasData(nsIURI* uri, bool* exists)
     520                 : {
     521               0 :     if (mOutputStreamTable.Get(uri, nsnull)) {
     522               0 :         *exists = true;
     523               0 :         return NS_OK;
     524                 :     }
     525               0 :     nsCAutoString spec(kXULCachePrefix);
     526               0 :     nsresult rv = PathifyURI(uri, spec);
     527               0 :     if (NS_FAILED(rv)) {
     528               0 :         *exists = false;
     529               0 :         return NS_OK;
     530                 :     }
     531               0 :     nsAutoArrayPtr<char> buf;
     532                 :     PRUint32 len;
     533               0 :     if (gStartupCache)
     534                 :         rv = gStartupCache->GetBuffer(spec.get(), getter_Transfers(buf), 
     535               0 :                                       &len);
     536                 :     else {
     537                 :         // We don't have everything we need to call BeginCaching and set up
     538                 :         // gStartupCache right now, but we just need to check the cache for 
     539                 :         // this URI.
     540               0 :         StartupCache* sc = StartupCache::GetSingleton();
     541               0 :         if (!sc) {
     542               0 :             *exists = false;
     543               0 :             return NS_OK;
     544                 :         }
     545               0 :         rv = sc->GetBuffer(spec.get(), getter_Transfers(buf), &len);
     546                 :     }
     547               0 :     *exists = NS_SUCCEEDED(rv);
     548               0 :     return NS_OK;
     549                 : }
     550                 : 
     551                 : static int
     552               0 : CachePrefChangedCallback(const char* aPref, void* aClosure)
     553                 : {
     554               0 :     bool wasEnabled = !gDisableXULDiskCache;
     555                 :     gDisableXULDiskCache =
     556                 :         Preferences::GetBool(kDisableXULCachePref,
     557               0 :                              gDisableXULDiskCache);
     558                 : 
     559               0 :     if (wasEnabled && gDisableXULDiskCache) {
     560               0 :         nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
     561                 : 
     562               0 :         if (cache)
     563               0 :             cache->AbortCaching();
     564                 :     }
     565               0 :     return 0;
     566                 : }
     567                 : 
     568                 : nsresult
     569               0 : nsXULPrototypeCache::BeginCaching(nsIURI* aURI)
     570                 : {
     571                 :     nsresult rv;
     572                 : 
     573               0 :     nsCAutoString path;
     574               0 :     aURI->GetPath(path);
     575               0 :     if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul")))
     576               0 :         return NS_ERROR_NOT_AVAILABLE;
     577                 : 
     578                 :     // Test gStartupCache to decide whether this is the first nsXULDocument
     579                 :     // participating in the serialization.  If gStartupCache is non-null, this document
     580                 :     // must not be first, but it can join the process.  Examples of
     581                 :     // multiple master documents participating include hiddenWindow.xul and
     582                 :     // navigator.xul on the Mac, and multiple-app-component (e.g., mailnews
     583                 :     // and browser) startup due to command-line arguments.
     584                 :     //
     585               0 :     if (gStartupCache) {
     586               0 :         mCacheURITable.Put(aURI, 1);
     587                 : 
     588               0 :         return NS_OK;
     589                 :     }
     590                 : 
     591                 :     // Use a local to refer to the service till we're sure we succeeded, then
     592                 :     // commit to gStartupCache.
     593               0 :     StartupCache* startupCache = StartupCache::GetSingleton();
     594               0 :     if (!startupCache)
     595               0 :         return NS_ERROR_FAILURE;
     596                 : 
     597                 :     gDisableXULDiskCache =
     598               0 :         Preferences::GetBool(kDisableXULCachePref, gDisableXULDiskCache);
     599                 : 
     600                 :     Preferences::RegisterCallback(CachePrefChangedCallback,
     601               0 :                                   kDisableXULCachePref);
     602                 : 
     603               0 :     if (gDisableXULDiskCache)
     604               0 :         return NS_ERROR_NOT_AVAILABLE;
     605                 : 
     606                 :     // Get the chrome directory to validate against the one stored in the
     607                 :     // cache file, or to store there if we're generating a new file.
     608               0 :     nsCOMPtr<nsIFile> chromeDir;
     609               0 :     rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir));
     610               0 :     if (NS_FAILED(rv))
     611               0 :         return rv;
     612               0 :     nsCAutoString chromePath;
     613               0 :     rv = chromeDir->GetNativePath(chromePath);
     614               0 :     if (NS_FAILED(rv))
     615               0 :         return rv;
     616                 : 
     617                 :     // XXXbe we assume the first package's locale is the same as the locale of
     618                 :     // all subsequent packages of cached chrome URIs....
     619               0 :     nsCAutoString package;
     620               0 :     rv = aURI->GetHost(package);
     621               0 :     if (NS_FAILED(rv))
     622               0 :         return rv;
     623                 :     nsCOMPtr<nsIXULChromeRegistry> chromeReg
     624               0 :         = do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv);
     625               0 :     nsCAutoString locale;
     626               0 :     rv = chromeReg->GetSelectedLocale(package, locale);
     627               0 :     if (NS_FAILED(rv))
     628               0 :         return rv;
     629                 : 
     630               0 :     nsCAutoString fileChromePath, fileLocale;
     631                 :     
     632               0 :     nsAutoArrayPtr<char> buf;
     633                 :     PRUint32 len, amtRead;
     634               0 :     nsCOMPtr<nsIObjectInputStream> objectInput;
     635                 : 
     636                 :     rv = startupCache->GetBuffer(kXULCacheInfoKey, getter_Transfers(buf), 
     637               0 :                                  &len);
     638               0 :     if (NS_SUCCEEDED(rv))
     639               0 :         rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(objectInput));
     640                 :     
     641               0 :     if (NS_SUCCEEDED(rv)) {
     642               0 :         buf.forget();
     643               0 :         rv = objectInput->ReadCString(fileLocale);
     644               0 :         rv |= objectInput->ReadCString(fileChromePath);
     645               0 :         if (NS_FAILED(rv) ||
     646               0 :             (!fileChromePath.Equals(chromePath) ||
     647               0 :              !fileLocale.Equals(locale))) {
     648                 :             // Our cache won't be valid in this case, we'll need to rewrite.
     649                 :             // XXX This blows away work that other consumers (like
     650                 :             // mozJSComponentLoader) have done, need more fine-grained control.
     651               0 :             startupCache->InvalidateCache();
     652               0 :             rv = NS_ERROR_UNEXPECTED;
     653                 :         }
     654               0 :     } else if (rv != NS_ERROR_NOT_AVAILABLE)
     655                 :         // NS_ERROR_NOT_AVAILABLE is normal, usually if there's no cachefile.
     656               0 :         return rv;
     657                 : 
     658               0 :     if (NS_FAILED(rv)) {
     659                 :         // Either the cache entry was invalid or it didn't exist, so write it now.
     660               0 :         nsCOMPtr<nsIObjectOutputStream> objectOutput;
     661               0 :         nsCOMPtr<nsIInputStream> inputStream;
     662               0 :         nsCOMPtr<nsIStorageStream> storageStream;
     663               0 :         rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput),
     664               0 :                                                  getter_AddRefs(storageStream),
     665               0 :                                                  false);
     666               0 :         if (NS_SUCCEEDED(rv)) {
     667               0 :             rv = objectOutput->WriteStringZ(locale.get());
     668               0 :             rv |= objectOutput->WriteStringZ(chromePath.get());
     669               0 :             rv |= objectOutput->Close();
     670               0 :             rv |= storageStream->NewInputStream(0, getter_AddRefs(inputStream));
     671                 :         }
     672               0 :         if (NS_SUCCEEDED(rv))
     673               0 :             rv = inputStream->Available(&len);
     674                 :         
     675               0 :         if (NS_SUCCEEDED(rv)) {
     676               0 :             buf = new char[len];
     677               0 :             rv = inputStream->Read(buf, len, &amtRead);
     678               0 :             if (NS_SUCCEEDED(rv) && len == amtRead)
     679               0 :                 rv = startupCache->PutBuffer(kXULCacheInfoKey, buf, len);
     680                 :             else {
     681               0 :                 rv = NS_ERROR_UNEXPECTED;
     682                 :             }
     683                 :         }
     684                 : 
     685                 :         // Failed again, just bail.
     686               0 :         if (NS_FAILED(rv)) {
     687               0 :             startupCache->InvalidateCache();
     688               0 :             return NS_ERROR_FAILURE;
     689                 :         }
     690                 :     }
     691                 : 
     692                 :     // Success!  Insert this URI into the mCacheURITable
     693                 :     // and commit locals to globals.
     694               0 :     mCacheURITable.Put(aURI, 1);
     695                 : 
     696               0 :     gStartupCache = startupCache;
     697               0 :     return NS_OK;
     698                 : }
     699                 : 
     700                 : static PLDHashOperator
     701               0 : MarkXBLInCCGeneration(nsIURI* aKey, nsRefPtr<nsXBLDocumentInfo> &aDocInfo,
     702                 :                       void* aClosure)
     703                 : {
     704               0 :     PRUint32* gen = static_cast<PRUint32*>(aClosure);
     705               0 :     aDocInfo->MarkInCCGeneration(*gen);
     706               0 :     return PL_DHASH_NEXT;
     707                 : }
     708                 : 
     709                 : static PLDHashOperator
     710               0 : MarkXULInCCGeneration(nsIURI* aKey, nsRefPtr<nsXULPrototypeDocument> &aDoc,
     711                 :                       void* aClosure)
     712                 : {
     713               0 :     PRUint32* gen = static_cast<PRUint32*>(aClosure);
     714               0 :     aDoc->MarkInCCGeneration(*gen);
     715               0 :     return PL_DHASH_NEXT;
     716                 : }
     717                 : 
     718                 : void
     719             280 : nsXULPrototypeCache::MarkInCCGeneration(PRUint32 aGeneration)
     720                 : {
     721             280 :     mXBLDocTable.Enumerate(MarkXBLInCCGeneration, &aGeneration);
     722             280 :     mPrototypeTable.Enumerate(MarkXULInCCGeneration, &aGeneration);
     723             280 : }

Generated by: LCOV version 1.7