diff --git a/netwerk/cache2/AppCacheStorage.cpp b/netwerk/cache2/AppCacheStorage.cpp index 75763237d01..69c03fb0f18 100644 --- a/netwerk/cache2/AppCacheStorage.cpp +++ b/netwerk/cache2/AppCacheStorage.cpp @@ -84,6 +84,13 @@ NS_IMETHODIMP AppCacheStorage::AsyncOpenURI(nsIURI *aURI, return NS_OK; } +NS_IMETHODIMP AppCacheStorage::Exists(nsIURI *aURI, const nsACString & aIdExtension, + bool *aResult) +{ + *aResult = false; + return NS_ERROR_NOT_AVAILABLE; +} + NS_IMETHODIMP AppCacheStorage::AsyncDoomURI(nsIURI *aURI, const nsACString & aIdExtension, nsICacheEntryDoomCallback* aCallback) { diff --git a/netwerk/cache2/CacheStorage.cpp b/netwerk/cache2/CacheStorage.cpp index e7350fbf9d5..6844be36a7f 100644 --- a/netwerk/cache2/CacheStorage.cpp +++ b/netwerk/cache2/CacheStorage.cpp @@ -104,6 +104,26 @@ NS_IMETHODIMP CacheStorage::AsyncOpenURI(nsIURI *aURI, return NS_OK; } + +NS_IMETHODIMP CacheStorage::Exists(nsIURI *aURI, const nsACString & aIdExtension, + bool *aResult) +{ + NS_ENSURE_ARG(aURI); + NS_ENSURE_ARG(aResult); + + if (!CacheStorageService::Self()) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv; + + nsCOMPtr noRefURI; + rv = aURI->CloneIgnoringRef(getter_AddRefs(noRefURI)); + NS_ENSURE_SUCCESS(rv, rv); + + return CacheStorageService::Self()->CheckStorageEntry( + this, noRefURI, aIdExtension, aResult); +} + NS_IMETHODIMP CacheStorage::AsyncDoomURI(nsIURI *aURI, const nsACString & aIdExtension, nsICacheEntryDoomCallback* aCallback) { diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp index ffbcdf80761..d5649393ac9 100644 --- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -1341,6 +1341,67 @@ CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey, return NS_OK; } +nsresult +CacheStorageService::CheckStorageEntry(CacheStorage const* aStorage, + nsIURI* aURI, + const nsACString & aIdExtension, + bool* aResult) +{ + nsresult rv; + + nsAutoCString contextKey; + CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); + + if (!aStorage->WriteToDisk()) { + AppendMemoryStorageID(contextKey); + } + +#ifdef PR_LOGGING + nsAutoCString uriSpec; + aURI->GetAsciiSpec(uriSpec); + LOG(("CacheStorageService::CheckStorageEntry [uri=%s, eid=%s, contextKey=%s]", + uriSpec.get(), aIdExtension.BeginReading(), contextKey.get())); +#endif + + { + mozilla::MutexAutoLock lock(mLock); + + NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); + + nsAutoCString entryKey; + rv = CacheEntry::HashingKey(EmptyCString(), aIdExtension, aURI, entryKey); + NS_ENSURE_SUCCESS(rv, rv); + + CacheEntryTable* entries; + if ((*aResult = sGlobalEntryTables->Get(contextKey, &entries)) && + entries->GetWeak(entryKey, aResult)) { + LOG((" found in hash tables")); + return NS_OK; + } + } + + if (!aStorage->WriteToDisk()) { + // Memory entry, nothing more to do. + LOG((" not found in hash tables")); + return NS_OK; + } + + // Disk entry, not found in the hashtable, check the index. + nsAutoCString fileKey; + rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey); + + CacheIndex::EntryStatus status; + rv = CacheIndex::HasEntry(fileKey, &status); + if (NS_FAILED(rv) || status == CacheIndex::DO_NOT_KNOW) { + LOG((" index doesn't know, rv=0x%08x", rv)); + return NS_ERROR_NOT_AVAILABLE; + } + + *aResult = status == CacheIndex::EXISTS; + LOG((" %sfound in index", *aResult ? "" : "not ")); + return NS_OK; +} + namespace { // anon class CacheEntryDoomByKeyCallback : public CacheFileIOListener diff --git a/netwerk/cache2/CacheStorageService.h b/netwerk/cache2/CacheStorageService.h index 3bc5a9c63b9..75f828b77d0 100644 --- a/netwerk/cache2/CacheStorageService.h +++ b/netwerk/cache2/CacheStorageService.h @@ -159,6 +159,15 @@ private: bool aReplace, CacheEntryHandle** aResult); + /** + * Check existance of an entry. This may throw NS_ERROR_NOT_AVAILABLE + * when the information cannot be obtained synchronously w/o blocking. + */ + nsresult CheckStorageEntry(CacheStorage const* aStorage, + nsIURI* aURI, + const nsACString & aIdExtension, + bool* aResult); + /** * Removes the entry from the related entry hash table, if still present * and returns it. diff --git a/netwerk/cache2/OldWrappers.cpp b/netwerk/cache2/OldWrappers.cpp index 23c4c9b80fa..a039861d644 100644 --- a/netwerk/cache2/OldWrappers.cpp +++ b/netwerk/cache2/OldWrappers.cpp @@ -943,6 +943,12 @@ NS_IMETHODIMP _OldStorage::AsyncOpenURI(nsIURI *aURI, return NS_OK; } +NS_IMETHODIMP _OldStorage::Exists(nsIURI *aURI, const nsACString & aIdExtension, + bool *aResult) +{ + return NS_ERROR_NOT_AVAILABLE; +} + NS_IMETHODIMP _OldStorage::AsyncDoomURI(nsIURI *aURI, const nsACString & aIdExtension, nsICacheEntryDoomCallback* aCallback) { diff --git a/netwerk/cache2/nsICacheStorage.idl b/netwerk/cache2/nsICacheStorage.idl index ca6f948b085..f8d681d300e 100644 --- a/netwerk/cache2/nsICacheStorage.idl +++ b/netwerk/cache2/nsICacheStorage.idl @@ -14,7 +14,7 @@ interface nsICacheStorageVisitor; * in-mem+on-disk, in-mem+on-disk+app-cache or just a specific * app-cache storage. */ -[scriptable, uuid(d983ba0c-433f-4017-abc1-93af737c82e4)] +[scriptable, uuid(d9006881-a536-4ce3-bc48-e7f94b40a690)] interface nsICacheStorage : nsISupports { /** @@ -77,6 +77,15 @@ interface nsICacheStorage : nsISupports in uint32_t aFlags, in nsICacheEntryOpenCallback aCallback); + /** + * Synchronously check on existance of an entry. In case of disk entries + * this uses information from the cache index. When the index data are not + * up to date or index is still building, NS_ERROR_NOT_AVAILABLE is thrown. + * The same error may throw any storage implementation that cannot determine + * entry state without blocking the caller. + */ + boolean exists(in nsIURI aURI, in ACString aIdExtension); + /** * Asynchronously removes an entry belonging to the URI from the cache. */ diff --git a/netwerk/test/unit/test_cache2-24-exists.js b/netwerk/test/unit/test_cache2-24-exists.js new file mode 100644 index 00000000000..6000bffd9ad --- /dev/null +++ b/netwerk/test/unit/test_cache2-24-exists.js @@ -0,0 +1,33 @@ +Components.utils.import('resource://gre/modules/LoadContextInfo.jsm'); + +function run_test() +{ + do_get_profile(); + + var mc = new MultipleCallbacks(2, function() { + var mem = getCacheStorage("memory"); + var disk = getCacheStorage("disk"); + + do_check_true(disk.exists(createURI("http://m1/"), "")); + do_check_true(mem.exists(createURI("http://m1/"), "")); + do_check_false(mem.exists(createURI("http://m2/"), "")); + do_check_true(disk.exists(createURI("http://d1/"), "")); + do_check_throws_nsIException(() => disk.exists(createURI("http://d2/"), ""), 'NS_ERROR_NOT_AVAILABLE'); + + finish_cache2_test(); + }); + + asyncOpenCacheEntry("http://d1/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, LoadContextInfo.default, + new OpenCallback(NEW | WAITFORWRITE, "meta", "data", function(entry) { + mc.fired(); + }) + ); + + asyncOpenCacheEntry("http://m1/", "memory", Ci.nsICacheStorage.OPEN_NORMALLY, LoadContextInfo.default, + new OpenCallback(NEW | WAITFORWRITE, "meta", "data", function(entry) { + mc.fired(); + }) + ); + + do_test_pending(); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index eae2ba94d68..d2b67b72b62 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -58,6 +58,9 @@ skip-if = os == "android" [test_cache2-21-anon-storage.js] [test_cache2-22-anon-visit.js] [test_cache2-23-read-over-chunk.js] +[test_cache2-24-exists.js] +# Bug 675039, comment 6: "The difference is that the memory cache is disabled in Armv6 builds." +skip-if = os == "android" [test_304_responses.js] # Bug 675039: test hangs on Android-armv6 skip-if = os == "android"