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 FastLoad code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
23 * Brendan Eich <brendan@mozilla.org> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
44 #include "nsAppDirectoryServiceDefs.h"
45 #include "nsAutoLock.h"
47 #include "nsFastLoadFile.h"
48 #include "nsFastLoadService.h"
51 #include "nsIComponentManager.h"
52 #include "nsIEnumerator.h"
53 #include "nsIFastLoadFileControl.h"
55 #include "nsIObjectInputStream.h"
56 #include "nsIObjectOutputStream.h"
57 #include "nsISeekableStream.h"
58 #include "nsISupports.h"
60 NS_IMPL_THREADSAFE_ISUPPORTS1(nsFastLoadService
, nsIFastLoadService
)
62 nsFastLoadService::nsFastLoadService()
64 mFastLoadPtrMap(nsnull
),
69 nsFastLoadService::~nsFastLoadService()
72 mInputStream
->Close();
74 mOutputStream
->Close();
77 PL_DHashTableDestroy(mFastLoadPtrMap
);
79 PR_DestroyLock(mLock
);
83 nsFastLoadService::Create(nsISupports
*aOuter
, REFNSIID aIID
, void **aResult
)
87 return NS_ERROR_NO_AGGREGATION
;
89 nsFastLoadService
* fastLoadService
= new nsFastLoadService();
91 return NS_ERROR_OUT_OF_MEMORY
;
93 fastLoadService
->mLock
= PR_NewLock();
94 if (!fastLoadService
->mLock
) {
95 delete fastLoadService
;
96 return NS_ERROR_OUT_OF_MEMORY
;
99 NS_ADDREF(fastLoadService
);
100 nsresult rv
= fastLoadService
->QueryInterface(aIID
, aResult
);
101 NS_RELEASE(fastLoadService
);
106 nsFastLoadService::NewFastLoadFile(const char* aBaseName
, nsIFile
* *aResult
)
110 // Try "ProfDS" first, so that we can get the profile directory
112 nsCOMPtr
<nsIFile
> profFile
;
113 rv
= NS_GetSpecialDirectory("ProfDS",
114 getter_AddRefs(profFile
));
116 // The directory service doesn't know about "ProfDS", so just ask
117 // for the regular profile directory key. Note, however, that this
118 // will fail if a profile hasn't yet been selected.
119 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
120 getter_AddRefs(profFile
));
126 // Try "ProfLDS" first, so that we can get the local profile directory
128 nsCOMPtr
<nsIFile
> file
;
129 rv
= NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(file
));
131 // The directory service doesn't know about "ProfLDS", so just ask
132 // for the regular local profile directory key. Note, however, that
133 // this will fail if a profile hasn't yet been selected.
134 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR
,
135 getter_AddRefs(file
));
141 rv
= file
->Equals(profFile
, &sameDir
);
145 nsCAutoString
name(aBaseName
);
146 name
+= PLATFORM_FASL_SUFFIX
;
147 rv
= file
->AppendNative(name
);
152 // Cleanup any pre-existing fastload files that may live in the profile
153 // directory from previous versions of the code that didn't store them
154 // in the profile temp directory.
155 rv
= profFile
->AppendNative(name
);
156 if (NS_SUCCEEDED(rv
))
157 profFile
->Remove(PR_FALSE
); // OK if this fails
166 nsFastLoadService::NewInputStream(nsIInputStream
* aSrcStream
,
167 nsIObjectInputStream
* *aResult
)
169 nsAutoLock
lock(mLock
);
171 nsCOMPtr
<nsIObjectInputStream
> stream
;
172 nsresult rv
= NS_NewFastLoadFileReader(getter_AddRefs(stream
), aSrcStream
);
182 nsFastLoadService::NewOutputStream(nsIOutputStream
* aDestStream
,
183 nsIObjectOutputStream
* *aResult
)
185 nsAutoLock
lock(mLock
);
187 return NS_NewFastLoadFileWriter(aResult
, aDestStream
, mFileIO
);
191 nsFastLoadService::GetInputStream(nsIObjectInputStream
* *aResult
)
193 NS_IF_ADDREF(*aResult
= mInputStream
);
198 nsFastLoadService::SetInputStream(nsIObjectInputStream
* aStream
)
200 nsAutoLock
lock(mLock
);
201 mInputStream
= aStream
;
206 nsFastLoadService::GetOutputStream(nsIObjectOutputStream
* *aResult
)
208 NS_IF_ADDREF(*aResult
= mOutputStream
);
213 nsFastLoadService::SetOutputStream(nsIObjectOutputStream
* aStream
)
215 nsAutoLock
lock(mLock
);
216 mOutputStream
= aStream
;
221 nsFastLoadService::GetFileIO(nsIFastLoadFileIO
* *aResult
)
223 NS_IF_ADDREF(*aResult
= mFileIO
);
228 nsFastLoadService::SetFileIO(nsIFastLoadFileIO
* aFileIO
)
230 nsAutoLock
lock(mLock
);
236 nsFastLoadService::GetDirection(PRInt32
*aResult
)
238 *aResult
= mDirection
;
243 nsFastLoadService::HasMuxedDocument(const char* aURISpec
, PRBool
*aResult
)
245 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
246 nsCOMPtr
<nsIFastLoadFileControl
> control
;
249 nsAutoLock
lock(mLock
);
252 control
= do_QueryInterface(mInputStream
);
254 rv
= control
->HasMuxedDocument(aURISpec
, aResult
);
257 if (! *aResult
&& mOutputStream
) {
258 control
= do_QueryInterface(mOutputStream
);
260 rv
= control
->HasMuxedDocument(aURISpec
, aResult
);
267 nsFastLoadService::StartMuxedDocument(nsISupports
* aURI
, const char* aURISpec
,
268 PRInt32 aDirectionFlags
)
270 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
271 nsCOMPtr
<nsIFastLoadFileControl
> control
;
272 nsAutoLock
lock(mLock
);
274 // Try for an input stream first, in case aURISpec's data is multiplexed
275 // in the current FastLoad file.
276 if ((aDirectionFlags
& NS_FASTLOAD_READ
) && mInputStream
) {
277 control
= do_QueryInterface(mInputStream
);
279 // If aURISpec is not in the multiplex, control->StartMuxedDocument
280 // will return NS_ERROR_NOT_AVAILABLE.
281 rv
= control
->StartMuxedDocument(aURI
, aURISpec
);
282 if (NS_SUCCEEDED(rv
) || rv
!= NS_ERROR_NOT_AVAILABLE
)
285 // Ok, aURISpec is not in the existing mux. If we have no output
286 // stream yet, wrap the reader with a FastLoad file updater.
287 if (!mOutputStream
&& mFileIO
) {
288 nsCOMPtr
<nsIOutputStream
> output
;
289 rv
= mFileIO
->GetOutputStream(getter_AddRefs(output
));
293 // NB: mInputStream must be an nsFastLoadFileReader!
294 rv
= NS_NewFastLoadFileUpdater(getter_AddRefs(mOutputStream
),
301 if (aDirectionFlags
== NS_FASTLOAD_READ
) {
302 // Tell our caller to re-start multiplexing, rather than attempt
303 // to select and deserialize now.
304 return NS_ERROR_NOT_AVAILABLE
;
309 if ((aDirectionFlags
& NS_FASTLOAD_WRITE
) && mOutputStream
) {
310 control
= do_QueryInterface(mOutputStream
);
312 rv
= control
->StartMuxedDocument(aURI
, aURISpec
);
318 nsFastLoadService::SelectMuxedDocument(nsISupports
* aURI
, nsISupports
** aResult
)
320 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
321 nsCOMPtr
<nsIFastLoadFileControl
> control
;
322 nsAutoLock
lock(mLock
);
324 // Try to select the reader, if any; then only if the URI was not in the
325 // file already, select the writer/updater.
327 control
= do_QueryInterface(mInputStream
);
329 rv
= control
->SelectMuxedDocument(aURI
, aResult
);
330 if (NS_SUCCEEDED(rv
))
331 mDirection
= NS_FASTLOAD_READ
;
335 if (rv
== NS_ERROR_NOT_AVAILABLE
&& mOutputStream
) {
336 control
= do_QueryInterface(mOutputStream
);
338 rv
= control
->SelectMuxedDocument(aURI
, aResult
);
339 if (NS_SUCCEEDED(rv
))
340 mDirection
= NS_FASTLOAD_WRITE
;
348 nsFastLoadService::EndMuxedDocument(nsISupports
* aURI
)
350 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
351 nsCOMPtr
<nsIFastLoadFileControl
> control
;
352 nsAutoLock
lock(mLock
);
354 // Try to end the document identified by aURI in the reader, if any; then
355 // only if the URI was not in the file already, end the writer/updater.
357 control
= do_QueryInterface(mInputStream
);
359 rv
= control
->EndMuxedDocument(aURI
);
362 if (rv
== NS_ERROR_NOT_AVAILABLE
&& mOutputStream
) {
363 control
= do_QueryInterface(mOutputStream
);
365 rv
= control
->EndMuxedDocument(aURI
);
373 nsFastLoadService::AddDependency(nsIFile
* aFile
)
375 nsAutoLock
lock(mLock
);
377 nsCOMPtr
<nsIFastLoadWriteControl
> control(do_QueryInterface(mOutputStream
));
379 return NS_ERROR_NOT_AVAILABLE
;
381 return control
->AddDependency(aFile
);
385 nsFastLoadService::ComputeChecksum(nsIFile
* aFile
,
386 nsIFastLoadReadControl
* aControl
,
390 nsresult rv
= aFile
->GetNativePath(path
);
394 nsCStringKey
key(path
);
395 PRUint32 checksum
= NS_PTR_TO_INT32(mChecksumTable
.Get(&key
));
397 *aChecksum
= checksum
;
401 rv
= aControl
->ComputeChecksum(&checksum
);
405 mChecksumTable
.Put(&key
, NS_INT32_TO_PTR(checksum
));
406 *aChecksum
= checksum
;
411 nsFastLoadService::CacheChecksum(nsIFile
* aFile
, nsIObjectOutputStream
*aStream
)
413 nsCOMPtr
<nsIFastLoadFileControl
> control(do_QueryInterface(aStream
));
415 return NS_ERROR_FAILURE
;
418 nsresult rv
= control
->GetChecksum(&checksum
);
423 rv
= aFile
->GetNativePath(path
);
427 nsCStringKey
key(path
);
428 mChecksumTable
.Put(&key
, NS_INT32_TO_PTR(checksum
));
432 struct nsFastLoadPtrEntry
: public PLDHashEntryHdr
{
433 nsISupports
** mPtrAddr
; // key, must come first for PL_DHashGetStubOps
438 nsFastLoadService::GetFastLoadReferent(nsISupports
* *aPtrAddr
)
440 NS_ASSERTION(*aPtrAddr
== nsnull
,
441 "aPtrAddr doesn't point to null nsFastLoadPtr<T>::mRawAddr?");
443 nsAutoLock
lock(mLock
);
444 if (!mFastLoadPtrMap
|| !mInputStream
)
447 nsFastLoadPtrEntry
* entry
=
448 static_cast<nsFastLoadPtrEntry
*>
449 (PL_DHashTableOperate(mFastLoadPtrMap
, aPtrAddr
,
451 if (PL_DHASH_ENTRY_IS_FREE(entry
))
455 nsCOMPtr
<nsISeekableStream
> seekable(do_QueryInterface(mInputStream
));
457 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, entry
->mOffset
);
461 rv
= mInputStream
->ReadObject(PR_TRUE
, aPtrAddr
);
465 // Shrink the table if half the entries are removed sentinels.
466 PRUint32 size
= PL_DHASH_TABLE_SIZE(mFastLoadPtrMap
);
467 if (mFastLoadPtrMap
->removedCount
>= (size
>> 2))
468 PL_DHashTableOperate(mFastLoadPtrMap
, entry
, PL_DHASH_REMOVE
);
470 PL_DHashTableRawRemove(mFastLoadPtrMap
, entry
);
476 nsFastLoadService::ReadFastLoadPtr(nsIObjectInputStream
* aInputStream
,
477 nsISupports
* *aPtrAddr
)
479 // nsFastLoadPtrs self-construct to null, so if we have a non-null value
480 // in our inout parameter, we must have been read already, alright!
486 nsAutoLock
lock(mLock
);
488 rv
= aInputStream
->Read32(&nextOffset
);
492 nsCOMPtr
<nsISeekableStream
> seekable(do_QueryInterface(aInputStream
));
494 return NS_ERROR_FAILURE
;
497 rv
= seekable
->Tell(&thisOffset
);
501 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, nextOffset
);
505 if (!mFastLoadPtrMap
) {
506 mFastLoadPtrMap
= PL_NewDHashTable(PL_DHashGetStubOps(), this,
507 sizeof(nsFastLoadPtrEntry
),
509 if (!mFastLoadPtrMap
)
510 return NS_ERROR_OUT_OF_MEMORY
;
513 nsFastLoadPtrEntry
* entry
=
514 static_cast<nsFastLoadPtrEntry
*>
515 (PL_DHashTableOperate(mFastLoadPtrMap
, aPtrAddr
,
517 NS_ASSERTION(entry
->mPtrAddr
== nsnull
, "duplicate nsFastLoadPtr?!");
519 entry
->mPtrAddr
= aPtrAddr
;
521 LL_L2UI(entry
->mOffset
, thisOffset
);
526 nsFastLoadService::WriteFastLoadPtr(nsIObjectOutputStream
* aOutputStream
,
527 nsISupports
* aObject
)
529 NS_ASSERTION(aObject
!= nsnull
, "writing an unread nsFastLoadPtr?!");
531 return NS_ERROR_UNEXPECTED
;
534 nsAutoLock
lock(mLock
); // serialize writes to aOutputStream
536 nsCOMPtr
<nsISeekableStream
> seekable(do_QueryInterface(aOutputStream
));
538 return NS_ERROR_FAILURE
;
541 rv
= seekable
->Tell(&saveOffset
);
545 rv
= aOutputStream
->Write32(0); // nextOffset placeholder
549 rv
= aOutputStream
->WriteObject(aObject
, PR_TRUE
);
554 rv
= seekable
->Tell(&nextOffset
);
558 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, saveOffset
);
562 rv
= aOutputStream
->Write32(nextOffset
);
566 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, nextOffset
);