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
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/
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
15 * The Original Code is Mozilla Communicator client code.
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.
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>
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.
41 * ***** END LICENSE BLOCK ***** */
43 #include "nsXULPrototypeCache.h"
45 #include "nsContentUtils.h"
47 #include "nsXULPrototypeDocument.h"
48 #include "nsICSSStyleSheet.h"
49 #include "nsIScriptRuntime.h"
50 #include "nsIServiceManager.h"
52 #include "nsIXBLDocumentInfo.h"
54 #include "nsIChromeRegistry.h"
55 #include "nsIFastLoadService.h"
56 #include "nsIFastLoadFileControl.h"
58 #include "nsIObjectInputStream.h"
59 #include "nsIObjectOutputStream.h"
60 #include "nsIObserverService.h"
62 #include "nsNetUtil.h"
63 #include "nsAppDirectoryServiceDefs.h"
67 static NS_DEFINE_CID(kXULPrototypeCacheCID
, NS_XULPROTOTYPECACHE_CID
);
69 static PRBool gDisableXULCache
= PR_FALSE
; // enabled by default
70 static const char kDisableXULCachePref
[] = "nglayout.debug.disable_xul_cache";
72 //----------------------------------------------------------------------
75 DisableXULCacheChangedCallback(const char* aPref
, void* aClosure
)
78 nsContentUtils::GetBoolPref(kDisableXULCachePref
, gDisableXULCache
);
80 // Flush the cache, regardless
81 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
88 //----------------------------------------------------------------------
91 nsIFastLoadService
* nsXULPrototypeCache::gFastLoadService
= nsnull
;
92 nsIFile
* nsXULPrototypeCache::gFastLoadFile
= nsnull
;
93 nsXULPrototypeCache
* nsXULPrototypeCache::sInstance
= nsnull
;
96 nsXULPrototypeCache::nsXULPrototypeCache()
101 nsXULPrototypeCache::~nsXULPrototypeCache()
105 NS_IF_RELEASE(gFastLoadService
); // don't need ReleaseService nowadays!
106 NS_IF_RELEASE(gFastLoadFile
);
110 NS_IMPL_THREADSAFE_ISUPPORTS2(nsXULPrototypeCache
,
111 nsIXULPrototypeCache
,
116 NS_NewXULPrototypeCache(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
)
118 NS_PRECONDITION(! aOuter
, "no aggregation");
120 return NS_ERROR_NO_AGGREGATION
;
122 nsRefPtr
<nsXULPrototypeCache
> result
= new nsXULPrototypeCache();
124 return NS_ERROR_OUT_OF_MEMORY
;
126 if (!(result
->mPrototypeTable
.Init() &&
127 result
->mStyleSheetTable
.Init() &&
128 result
->mScriptTable
.Init() &&
129 result
->mXBLDocTable
.Init() &&
130 result
->mFastLoadURITable
.Init())) {
131 return NS_ERROR_OUT_OF_MEMORY
;
134 // XXX Ignore return values.
136 nsContentUtils::GetBoolPref(kDisableXULCachePref
, gDisableXULCache
);
137 nsContentUtils::RegisterPrefCallback(kDisableXULCachePref
,
138 DisableXULCacheChangedCallback
,
141 nsresult rv
= result
->QueryInterface(aIID
, aResult
);
143 nsCOMPtr
<nsIObserverService
> obsSvc(do_GetService("@mozilla.org/observer-service;1"));
144 if (obsSvc
&& NS_SUCCEEDED(rv
)) {
145 nsXULPrototypeCache
*p
= result
;
146 obsSvc
->AddObserver(p
, "chrome-flush-skin-caches", PR_FALSE
);
147 obsSvc
->AddObserver(p
, "chrome-flush-caches", PR_FALSE
);
153 /* static */ nsXULPrototypeCache
*
154 nsXULPrototypeCache::GetInstance()
156 // Theoretically this can return nsnull and callers should handle that.
158 nsIXULPrototypeCache
* cache
;
160 CallGetService(kXULPrototypeCacheCID
, &cache
);
162 sInstance
= static_cast<nsXULPrototypeCache
*>(cache
);
167 /* static */ nsIFastLoadService
*
168 nsXULPrototypeCache::GetFastLoadService()
170 return gFastLoadService
;
173 //----------------------------------------------------------------------
176 nsXULPrototypeCache::Observe(nsISupports
* aSubject
,
178 const PRUnichar
*aData
)
180 if (!strcmp(aTopic
, "chrome-flush-skin-caches")) {
183 else if (!strcmp(aTopic
, "chrome-flush-caches")) {
187 NS_WARNING("Unexpected observer topic.");
192 nsXULPrototypeDocument
*
193 nsXULPrototypeCache::GetPrototype(nsIURI
* aURI
)
195 nsXULPrototypeDocument
* protoDoc
= mPrototypeTable
.GetWeak(aURI
);
198 // No prototype in XUL memory cache. Spin up FastLoad Service and
199 // look in FastLoad file.
200 nsresult rv
= StartFastLoad(aURI
);
201 if (NS_SUCCEEDED(rv
)) {
202 nsCOMPtr
<nsIObjectInputStream
> objectInput
;
203 gFastLoadService
->GetInputStream(getter_AddRefs(objectInput
));
205 rv
= StartFastLoadingURI(aURI
, nsIFastLoadService::NS_FASTLOAD_READ
);
206 if (NS_SUCCEEDED(rv
)) {
207 nsCOMPtr
<nsIURI
> oldURI
;
208 gFastLoadService
->SelectMuxedDocument(aURI
, getter_AddRefs(oldURI
));
210 // Create a new prototype document.
211 nsRefPtr
<nsXULPrototypeDocument
> newProto
;
212 rv
= NS_NewXULPrototypeDocument(getter_AddRefs(newProto
));
213 if (NS_FAILED(rv
)) return nsnull
;
215 rv
= newProto
->Read(objectInput
);
216 if (NS_SUCCEEDED(rv
)) {
217 rv
= PutPrototype(newProto
);
221 gFastLoadService
->EndMuxedDocument(aURI
);
226 RemoveFromFastLoadSet(aURI
);
235 nsXULPrototypeCache::PutPrototype(nsXULPrototypeDocument
* aDocument
)
237 nsCOMPtr
<nsIURI
> uri
= aDocument
->GetURI();
238 // Put() releases any old value and addrefs the new one
239 NS_ENSURE_TRUE(mPrototypeTable
.Put(uri
, aDocument
), NS_ERROR_OUT_OF_MEMORY
);
245 nsXULPrototypeCache::PutStyleSheet(nsICSSStyleSheet
* aStyleSheet
)
247 nsCOMPtr
<nsIURI
> uri
;
248 nsresult rv
= aStyleSheet
->GetSheetURI(getter_AddRefs(uri
));
252 NS_ENSURE_TRUE(mStyleSheetTable
.Put(uri
, aStyleSheet
),
253 NS_ERROR_OUT_OF_MEMORY
);
260 nsXULPrototypeCache::GetScript(nsIURI
* aURI
, PRUint32
*aLangID
)
262 CacheScriptEntry entry
;
263 if (!mScriptTable
.Get(aURI
, &entry
)) {
264 *aLangID
= nsIProgrammingLanguage::UNKNOWN
;
267 *aLangID
= entry
.mScriptTypeID
;
268 return entry
.mScriptObject
;
273 static PLDHashOperator
274 ReleaseScriptObjectCallback(nsIURI
* aKey
, CacheScriptEntry
&aData
, void* aClosure
)
276 nsCOMPtr
<nsIScriptRuntime
> rt
;
277 if (NS_SUCCEEDED(NS_GetScriptRuntimeByID(aData
.mScriptTypeID
, getter_AddRefs(rt
))))
278 rt
->DropScriptObject(aData
.mScriptObject
);
279 return PL_DHASH_REMOVE
;
283 nsXULPrototypeCache::PutScript(nsIURI
* aURI
, PRUint32 aLangID
, void* aScriptObject
)
285 CacheScriptEntry existingEntry
;
286 if (mScriptTable
.Get(aURI
, &existingEntry
)) {
287 NS_WARNING("loaded the same script twice (bug 392650)");
289 // Reuse the callback used for enumeration in FlushScripts
290 ReleaseScriptObjectCallback(aURI
, existingEntry
, nsnull
);
293 CacheScriptEntry entry
= {aLangID
, aScriptObject
};
295 NS_ENSURE_TRUE(mScriptTable
.Put(aURI
, entry
), NS_ERROR_OUT_OF_MEMORY
);
297 // Lock the object from being gc'd until it is removed from the cache
298 nsCOMPtr
<nsIScriptRuntime
> rt
;
299 nsresult rv
= NS_GetScriptRuntimeByID(aLangID
, getter_AddRefs(rt
));
300 if (NS_SUCCEEDED(rv
))
301 rv
= rt
->HoldScriptObject(aScriptObject
);
302 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to GC lock the object");
304 // On failure doing the lock, we should remove the map entry?
309 nsXULPrototypeCache::FlushScripts()
311 // This callback will unlock each object so it can once again be gc'd.
312 // XXX - this might be slow - we fetch the runtime each and every object.
313 mScriptTable
.Enumerate(ReleaseScriptObjectCallback
, nsnull
);
318 nsXULPrototypeCache::PutXBLDocumentInfo(nsIXBLDocumentInfo
* aDocumentInfo
)
320 nsIURI
* uri
= aDocumentInfo
->DocumentURI();
322 nsCOMPtr
<nsIXBLDocumentInfo
> info
;
323 mXBLDocTable
.Get(uri
, getter_AddRefs(info
));
325 NS_ENSURE_TRUE(mXBLDocTable
.Put(uri
, aDocumentInfo
),
326 NS_ERROR_OUT_OF_MEMORY
);
331 static PLDHashOperator
332 FlushSkinXBL(nsIURI
* aKey
, nsCOMPtr
<nsIXBLDocumentInfo
>& aDocInfo
, void* aClosure
)
337 PLDHashOperator ret
= PL_DHASH_NEXT
;
339 if (!strncmp(str
.get(), "/skin", 5)) {
340 ret
= PL_DHASH_REMOVE
;
346 static PLDHashOperator
347 FlushSkinSheets(nsIURI
* aKey
, nsCOMPtr
<nsICSSStyleSheet
>& aSheet
, void* aClosure
)
349 nsCOMPtr
<nsIURI
> uri
;
350 aSheet
->GetSheetURI(getter_AddRefs(uri
));
354 PLDHashOperator ret
= PL_DHASH_NEXT
;
356 if (!strncmp(str
.get(), "/skin", 5)) {
357 // This is a skin binding. Add the key to the list.
358 ret
= PL_DHASH_REMOVE
;
363 static PLDHashOperator
364 FlushScopedSkinStylesheets(nsIURI
* aKey
, nsCOMPtr
<nsIXBLDocumentInfo
> &aDocInfo
, void* aClosure
)
366 aDocInfo
->FlushSkinStylesheets();
367 return PL_DHASH_NEXT
;
371 nsXULPrototypeCache::FlushSkinFiles()
373 // Flush out skin XBL files from the cache.
374 mXBLDocTable
.Enumerate(FlushSkinXBL
, nsnull
);
376 // Now flush out our skin stylesheets from the cache.
377 mStyleSheetTable
.Enumerate(FlushSkinSheets
, nsnull
);
379 // Iterate over all the remaining XBL and make sure cached
380 // scoped skin stylesheets are flushed and refetched by the
381 // prototype bindings.
382 mXBLDocTable
.Enumerate(FlushScopedSkinStylesheets
, nsnull
);
387 nsXULPrototypeCache::Flush()
389 mPrototypeTable
.Clear();
391 // Clear the script cache, as it refers to prototype-owned mJSObjects.
394 mStyleSheetTable
.Clear();
395 mXBLDocTable
.Clear();
400 nsXULPrototypeCache::IsEnabled()
402 return !gDisableXULCache
;
405 static PRBool gDisableXULFastLoad
= PR_FALSE
; // enabled by default
406 static PRBool gChecksumXULFastLoadFile
= PR_TRUE
; // XXXbe too paranoid
409 nsXULPrototypeCache::AbortFastLoads()
415 // Save a strong ref to the FastLoad file, so we can remove it after we
416 // close open streams to it.
417 nsCOMPtr
<nsIFile
> file
= gFastLoadFile
;
419 // Flush the XUL cache for good measure, in case we cached a bogus/downrev
423 // Clear the FastLoad set
424 mFastLoadURITable
.Clear();
426 if (! gFastLoadService
)
429 // Fetch the current input (if FastLoad file existed) or output (if we're
430 // creating the FastLoad file during this app startup) stream.
431 nsCOMPtr
<nsIObjectInputStream
> objectInput
;
432 nsCOMPtr
<nsIObjectOutputStream
> objectOutput
;
433 gFastLoadService
->GetInputStream(getter_AddRefs(objectInput
));
434 gFastLoadService
->GetOutputStream(getter_AddRefs(objectOutput
));
437 gFastLoadService
->SetOutputStream(nsnull
);
439 if (NS_SUCCEEDED(objectOutput
->Close()) && gChecksumXULFastLoadFile
)
440 gFastLoadService
->CacheChecksum(gFastLoadFile
,
445 // If this is the last of one or more XUL master documents loaded
446 // together at app startup, close the FastLoad service's singleton
448 gFastLoadService
->SetInputStream(nsnull
);
449 objectInput
->Close();
452 // Now rename or remove the file.
455 // Remove any existing Aborted.mfasl files generated in previous runs.
456 nsCOMPtr
<nsIFile
> existingAbortedFile
;
457 file
->Clone(getter_AddRefs(existingAbortedFile
));
458 if (existingAbortedFile
) {
459 existingAbortedFile
->SetLeafName(NS_LITERAL_STRING("Aborted.mfasl"));
460 PRBool fileExists
= PR_FALSE
;
461 existingAbortedFile
->Exists(&fileExists
);
463 existingAbortedFile
->Remove(PR_FALSE
);
465 file
->MoveToNative(nsnull
, NS_LITERAL_CSTRING("Aborted.mfasl"));
467 file
->Remove(PR_FALSE
);
471 // If the list is empty now, the FastLoad process is done.
472 NS_RELEASE(gFastLoadService
);
473 NS_RELEASE(gFastLoadFile
);
478 nsXULPrototypeCache::RemoveFromFastLoadSet(nsIURI
* aURI
)
480 mFastLoadURITable
.Remove(aURI
);
483 static const char kDisableXULFastLoadPref
[] = "nglayout.debug.disable_xul_fastload";
484 static const char kChecksumXULFastLoadFilePref
[] = "nglayout.debug.checksum_xul_fastload_file";
487 nsXULPrototypeCache::WritePrototype(nsXULPrototypeDocument
* aPrototypeDocument
)
489 nsresult rv
= NS_OK
, rv2
= NS_OK
;
491 // We're here before the FastLoad service has been initialized, probably because
492 // of the profile manager. Bail quietly, don't worry, we'll be back later.
493 if (! gFastLoadService
)
496 // Fetch the current input (if FastLoad file existed) or output (if we're
497 // creating the FastLoad file during this app startup) stream.
498 nsCOMPtr
<nsIObjectInputStream
> objectInput
;
499 nsCOMPtr
<nsIObjectOutputStream
> objectOutput
;
500 gFastLoadService
->GetInputStream(getter_AddRefs(objectInput
));
501 gFastLoadService
->GetOutputStream(getter_AddRefs(objectOutput
));
503 nsCOMPtr
<nsIURI
> protoURI
= aPrototypeDocument
->GetURI();
505 // Remove this document from the FastLoad table. We use the table's
506 // emptiness instead of a counter to decide when the FastLoad process
507 // has completed. When complete, we can write footer details to the
509 RemoveFromFastLoadSet(protoURI
);
511 PRInt32 count
= mFastLoadURITable
.Count();
514 rv
= StartFastLoadingURI(protoURI
, nsIFastLoadService::NS_FASTLOAD_WRITE
);
515 if (NS_SUCCEEDED(rv
)) {
516 // Re-select the URL of the current prototype, as out-of-line script loads
518 nsCOMPtr
<nsIURI
> oldURI
;
519 gFastLoadService
->SelectMuxedDocument(protoURI
, getter_AddRefs(oldURI
));
521 aPrototypeDocument
->Write(objectOutput
);
523 gFastLoadService
->EndMuxedDocument(protoURI
);
526 // If this is the last of one or more XUL master documents loaded
527 // together at app startup, close the FastLoad service's singleton
528 // output stream now.
530 // NB: we must close input after output, in case the output stream
531 // implementation needs to read from the input stream, to compute a
532 // FastLoad file checksum. In that case, the implementation used
533 // nsIFastLoadFileIO to get the corresponding input stream for this
536 gFastLoadService
->SetOutputStream(nsnull
);
537 rv
= objectOutput
->Close();
539 if (NS_SUCCEEDED(rv
) && gChecksumXULFastLoadFile
) {
540 rv
= gFastLoadService
->CacheChecksum(gFastLoadFile
,
547 // If this is the last of one or more XUL master documents loaded
548 // together at app startup, close the FastLoad service's singleton
551 gFastLoadService
->SetInputStream(nsnull
);
552 rv2
= objectInput
->Close();
556 // If the list is empty now, the FastLoad process is done.
558 NS_RELEASE(gFastLoadService
);
559 NS_RELEASE(gFastLoadFile
);
562 return NS_FAILED(rv
) ? rv
: rv2
;
567 nsXULPrototypeCache::StartFastLoadingURI(nsIURI
* aURI
, PRInt32 aDirectionFlags
)
571 nsCAutoString urlspec
;
572 rv
= aURI
->GetAsciiSpec(urlspec
);
573 if (NS_FAILED(rv
)) return rv
;
575 // If StartMuxedDocument returns NS_ERROR_NOT_AVAILABLE, then
576 // we must be reading the file, and urlspec was not associated
577 // with any multiplexed stream in it. The FastLoad service
578 // will therefore arrange to update the file, writing new data
579 // at the end while old (available) data continues to be read
580 // from the pre-existing part of the file.
581 return gFastLoadService
->StartMuxedDocument(aURI
, urlspec
.get(), aDirectionFlags
);
585 FastLoadPrefChangedCallback(const char* aPref
, void* aClosure
)
587 PRBool wasEnabled
= !gDisableXULFastLoad
;
588 gDisableXULFastLoad
=
589 nsContentUtils::GetBoolPref(kDisableXULFastLoadPref
,
590 gDisableXULFastLoad
);
592 if (wasEnabled
&& gDisableXULFastLoad
) {
593 static NS_DEFINE_CID(kXULPrototypeCacheCID
, NS_XULPROTOTYPECACHE_CID
);
594 nsCOMPtr
<nsIXULPrototypeCache
> cache
=
595 do_GetService(kXULPrototypeCacheCID
);
598 cache
->AbortFastLoads();
601 gChecksumXULFastLoadFile
=
602 nsContentUtils::GetBoolPref(kChecksumXULFastLoadFilePref
,
603 gChecksumXULFastLoadFile
);
609 class nsXULFastLoadFileIO
: public nsIFastLoadFileIO
612 nsXULFastLoadFileIO(nsIFile
* aFile
)
616 virtual ~nsXULFastLoadFileIO() {
620 NS_DECL_NSIFASTLOADFILEIO
622 nsCOMPtr
<nsIFile
> mFile
;
623 nsCOMPtr
<nsIInputStream
> mInputStream
;
624 nsCOMPtr
<nsIOutputStream
> mOutputStream
;
628 NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULFastLoadFileIO
, nsIFastLoadFileIO
)
632 nsXULFastLoadFileIO::GetInputStream(nsIInputStream
** aResult
)
634 if (! mInputStream
) {
636 nsCOMPtr
<nsIInputStream
> fileInput
;
637 rv
= NS_NewLocalFileInputStream(getter_AddRefs(fileInput
), mFile
);
638 if (NS_FAILED(rv
)) return rv
;
640 rv
= NS_NewBufferedInputStream(getter_AddRefs(mInputStream
),
642 XUL_DESERIALIZATION_BUFFER_SIZE
);
643 if (NS_FAILED(rv
)) return rv
;
646 NS_ADDREF(*aResult
= mInputStream
);
652 nsXULFastLoadFileIO::GetOutputStream(nsIOutputStream
** aResult
)
654 if (! mOutputStream
) {
655 PRInt32 ioFlags
= PR_WRONLY
;
657 ioFlags
|= PR_CREATE_FILE
| PR_TRUNCATE
;
660 nsCOMPtr
<nsIOutputStream
> fileOutput
;
661 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(fileOutput
), mFile
,
663 if (NS_FAILED(rv
)) return rv
;
665 rv
= NS_NewBufferedOutputStream(getter_AddRefs(mOutputStream
),
667 XUL_SERIALIZATION_BUFFER_SIZE
);
668 if (NS_FAILED(rv
)) return rv
;
671 NS_ADDREF(*aResult
= mOutputStream
);
676 nsXULPrototypeCache::StartFastLoad(nsIURI
* aURI
)
682 if (!StringEndsWith(path
, NS_LITERAL_CSTRING(".xul")))
683 return NS_ERROR_NOT_AVAILABLE
;
685 // Test gFastLoadFile to decide whether this is the first nsXULDocument
686 // participating in FastLoad. If gFastLoadFile is non-null, this document
687 // must not be first, but it can join the FastLoad process. Examples of
688 // multiple master documents participating include hiddenWindow.xul and
689 // navigator.xul on the Mac, and multiple-app-component (e.g., mailnews
690 // and browser) startup due to command-line arguments.
692 // XXXbe we should attempt to update the FastLoad file after startup!
694 // XXXbe we do not yet use nsFastLoadPtrs, but once we do, we must keep
695 // the FastLoad input stream open for the life of the app.
696 if (gFastLoadService
&& gFastLoadFile
) {
697 mFastLoadURITable
.Put(aURI
, 1);
702 // Use a local to refer to the service till we're sure we succeeded, then
703 // commit to gFastLoadService. Same for gFastLoadFile, which is used to
704 // delete the FastLoad file on abort.
705 nsCOMPtr
<nsIFastLoadService
> fastLoadService(do_GetFastLoadService());
706 if (! fastLoadService
)
707 return NS_ERROR_FAILURE
;
709 gDisableXULFastLoad
=
710 nsContentUtils::GetBoolPref(kDisableXULFastLoadPref
,
711 gDisableXULFastLoad
);
712 gChecksumXULFastLoadFile
=
713 nsContentUtils::GetBoolPref(kChecksumXULFastLoadFilePref
,
714 gChecksumXULFastLoadFile
);
715 nsContentUtils::RegisterPrefCallback(kDisableXULFastLoadPref
,
716 FastLoadPrefChangedCallback
,
718 nsContentUtils::RegisterPrefCallback(kChecksumXULFastLoadFilePref
,
719 FastLoadPrefChangedCallback
,
722 if (gDisableXULFastLoad
)
723 return NS_ERROR_NOT_AVAILABLE
;
725 // Get the chrome directory to validate against the one stored in the
726 // FastLoad file, or to store there if we're generating a new file.
727 nsCOMPtr
<nsIFile
> chromeDir
;
728 rv
= NS_GetSpecialDirectory(NS_APP_CHROME_DIR
, getter_AddRefs(chromeDir
));
731 nsCAutoString chromePath
;
732 rv
= chromeDir
->GetNativePath(chromePath
);
736 nsCOMPtr
<nsIFile
> file
;
737 rv
= fastLoadService
->NewFastLoadFile(XUL_FASTLOAD_FILE_BASENAME
,
738 getter_AddRefs(file
));
742 // Give the FastLoad service an object by which it can get or create a
743 // file output stream given an input stream on the same file.
744 nsXULFastLoadFileIO
* xio
= new nsXULFastLoadFileIO(file
);
745 nsCOMPtr
<nsIFastLoadFileIO
> io
= static_cast<nsIFastLoadFileIO
*>(xio
);
747 return NS_ERROR_OUT_OF_MEMORY
;
748 fastLoadService
->SetFileIO(io
);
750 nsCOMPtr
<nsIXULChromeRegistry
> chromeReg(do_GetService(NS_CHROMEREGISTRY_CONTRACTID
, &rv
));
754 // XXXbe we assume the first package's locale is the same as the locale of
755 // all subsequent packages of FastLoaded chrome URIs....
756 nsCAutoString package
;
757 rv
= aURI
->GetHost(package
);
761 nsCAutoString locale
;
762 rv
= chromeReg
->GetSelectedLocale(package
, locale
);
766 // Try to read an existent FastLoad file.
767 PRBool exists
= PR_FALSE
;
768 if (NS_SUCCEEDED(file
->Exists(&exists
)) && exists
) {
769 nsCOMPtr
<nsIInputStream
> input
;
770 rv
= io
->GetInputStream(getter_AddRefs(input
));
774 nsCOMPtr
<nsIObjectInputStream
> objectInput
;
775 rv
= fastLoadService
->NewInputStream(input
, getter_AddRefs(objectInput
));
777 if (NS_SUCCEEDED(rv
)) {
778 if (gChecksumXULFastLoadFile
) {
779 nsCOMPtr
<nsIFastLoadReadControl
>
780 readControl(do_QueryInterface(objectInput
));
782 // Verify checksum, using the fastLoadService's checksum
783 // cache to avoid computing more than once per session.
785 rv
= readControl
->GetChecksum(&checksum
);
786 if (NS_SUCCEEDED(rv
)) {
788 rv
= fastLoadService
->ComputeChecksum(file
,
791 if (NS_SUCCEEDED(rv
) && verified
!= checksum
) {
793 printf("bad FastLoad file checksum\n");
795 rv
= NS_ERROR_FAILURE
;
801 if (NS_SUCCEEDED(rv
)) {
802 // Get the XUL fastload file version number, which should be
803 // decremented whenever the XUL-specific file format changes
804 // (see public/nsIXULPrototypeCache.h for the #define).
805 PRUint32 xulFastLoadVersion
, jsByteCodeVersion
;
806 rv
= objectInput
->Read32(&xulFastLoadVersion
);
807 rv
|= objectInput
->Read32(&jsByteCodeVersion
);
808 if (NS_SUCCEEDED(rv
)) {
809 if (xulFastLoadVersion
!= XUL_FASTLOAD_FILE_VERSION
||
810 jsByteCodeVersion
!= JSXDR_BYTECODE_VERSION
) {
812 printf((xulFastLoadVersion
!= XUL_FASTLOAD_FILE_VERSION
)
813 ? "bad FastLoad file version\n"
814 : "bad JS bytecode version\n");
816 rv
= NS_ERROR_UNEXPECTED
;
818 nsCAutoString fileChromePath
, fileLocale
;
820 rv
= objectInput
->ReadCString(fileChromePath
);
821 rv
|= objectInput
->ReadCString(fileLocale
);
822 if (NS_SUCCEEDED(rv
) &&
823 (!fileChromePath
.Equals(chromePath
) ||
824 !fileLocale
.Equals(locale
))) {
825 rv
= NS_ERROR_UNEXPECTED
;
832 if (NS_SUCCEEDED(rv
)) {
833 fastLoadService
->SetInputStream(objectInput
);
835 // NB: we must close before attempting to remove, for non-Unix OSes
836 // that can't do open-unlink.
838 objectInput
->Close();
841 xio
->mInputStream
= nsnull
;
844 file
->MoveToNative(nsnull
, NS_LITERAL_CSTRING("Invalid.mfasl"));
846 file
->Remove(PR_FALSE
);
852 // FastLoad file not found, or invalid: write a new one.
854 nsCOMPtr
<nsIOutputStream
> output
;
855 rv
= io
->GetOutputStream(getter_AddRefs(output
));
859 nsCOMPtr
<nsIObjectOutputStream
> objectOutput
;
860 rv
= fastLoadService
->NewOutputStream(output
,
861 getter_AddRefs(objectOutput
));
862 if (NS_SUCCEEDED(rv
)) {
863 rv
= objectOutput
->Write32(XUL_FASTLOAD_FILE_VERSION
);
864 rv
|= objectOutput
->Write32(JSXDR_BYTECODE_VERSION
);
865 rv
|= objectOutput
->WriteStringZ(chromePath
.get());
866 rv
|= objectOutput
->WriteStringZ(locale
.get());
869 // Remove here even though some errors above will lead to a FastLoad
870 // file invalidation. Other errors (failure to note the dependency on
871 // installed-chrome.txt, e.g.) will not cause invalidation, and we may
872 // as well tidy up now.
875 objectOutput
->Close();
878 xio
->mOutputStream
= nsnull
;
880 file
->Remove(PR_FALSE
);
881 return NS_ERROR_FAILURE
;
884 fastLoadService
->SetOutputStream(objectOutput
);
887 // Success! Insert this URI into the mFastLoadURITable
888 // and commit locals to globals.
889 mFastLoadURITable
.Put(aURI
, 1);
891 NS_ADDREF(gFastLoadService
= fastLoadService
);
892 NS_ADDREF(gFastLoadFile
= file
);