mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-22 07:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
51
netwerk/cache/moz.build
vendored
Normal file
51
netwerk/cache/moz.build
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsICache.idl',
|
||||
'nsICacheEntryDescriptor.idl',
|
||||
'nsICacheListener.idl',
|
||||
'nsICacheService.idl',
|
||||
'nsICacheSession.idl',
|
||||
'nsICacheVisitor.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_cache'
|
||||
|
||||
EXPORTS += [
|
||||
'nsApplicationCacheService.h',
|
||||
'nsCacheService.h',
|
||||
'nsDeleteDir.h'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsApplicationCacheService.cpp',
|
||||
'nsCache.cpp',
|
||||
'nsCacheEntry.cpp',
|
||||
'nsCacheEntryDescriptor.cpp',
|
||||
'nsCacheMetaData.cpp',
|
||||
'nsCacheService.cpp',
|
||||
'nsCacheSession.cpp',
|
||||
'nsCacheUtils.cpp',
|
||||
'nsDeleteDir.cpp',
|
||||
'nsDiskCacheBinding.cpp',
|
||||
'nsDiskCacheBlockFile.cpp',
|
||||
'nsDiskCacheDevice.cpp',
|
||||
'nsDiskCacheDeviceSQL.cpp',
|
||||
'nsDiskCacheEntry.cpp',
|
||||
'nsDiskCacheMap.cpp',
|
||||
'nsDiskCacheStreams.cpp',
|
||||
'nsMemoryCacheDevice.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
29
netwerk/cache/nsApplicationCache.h
vendored
Normal file
29
netwerk/cache/nsApplicationCache.h
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et cin: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
class nsApplicationCache : public nsIApplicationCache
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIAPPLICATIONCACHE
|
||||
|
||||
nsApplicationCache(nsOfflineCacheDevice *device,
|
||||
const nsACString &group,
|
||||
const nsACString &clientID);
|
||||
|
||||
nsApplicationCache();
|
||||
|
||||
void MarkInvalid();
|
||||
|
||||
private:
|
||||
virtual ~nsApplicationCache();
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> mDevice;
|
||||
nsCString mGroup;
|
||||
nsCString mClientID;
|
||||
bool mValid;
|
||||
};
|
||||
|
||||
268
netwerk/cache/nsApplicationCacheService.cpp
vendored
Normal file
268
netwerk/cache/nsApplicationCacheService.cpp
vendored
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsDiskCache.h"
|
||||
#include "nsDiskCacheDeviceSQL.h"
|
||||
#include "nsCacheService.h"
|
||||
#include "nsApplicationCacheService.h"
|
||||
#include "nsCRT.h"
|
||||
#include "mozIApplicationClearPrivateDataParams.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "mozilla/LoadContextInfo.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsApplicationCacheService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsApplicationCacheService, nsIApplicationCacheService)
|
||||
|
||||
nsApplicationCacheService::nsApplicationCacheService()
|
||||
{
|
||||
nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID);
|
||||
mCacheService = nsCacheService::GlobalInstance();
|
||||
}
|
||||
|
||||
nsApplicationCacheService::~nsApplicationCacheService()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::BuildGroupIDForInfo(
|
||||
nsIURI *aManifestURL,
|
||||
nsILoadContextInfo *aLoadContextInfo,
|
||||
nsACString &_result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString originSuffix;
|
||||
if (aLoadContextInfo) {
|
||||
aLoadContextInfo->OriginAttributesPtr()->CreateSuffix(originSuffix);
|
||||
}
|
||||
|
||||
rv = nsOfflineCacheDevice::BuildApplicationCacheGroupID(
|
||||
aManifestURL, originSuffix, _result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::BuildGroupIDForSuffix(
|
||||
nsIURI *aManifestURL,
|
||||
nsACString const &aOriginSuffix,
|
||||
nsACString &_result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = nsOfflineCacheDevice::BuildApplicationCacheGroupID(
|
||||
aManifestURL, aOriginSuffix, _result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::CreateApplicationCache(const nsACString &group,
|
||||
nsIApplicationCache **out)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->CreateApplicationCache(group, out);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::CreateCustomApplicationCache(const nsACString & group,
|
||||
nsIFile *profileDir,
|
||||
int32_t quota,
|
||||
nsIApplicationCache **out)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetCustomOfflineDevice(profileDir,
|
||||
quota,
|
||||
getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->CreateApplicationCache(group, out);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::GetApplicationCache(const nsACString &clientID,
|
||||
nsIApplicationCache **out)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->GetApplicationCache(clientID, out);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::GetActiveCache(const nsACString &group,
|
||||
nsIApplicationCache **out)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->GetActiveCache(group, out);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::DeactivateGroup(const nsACString &group)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->DeactivateGroup(group);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::ChooseApplicationCache(const nsACString &key,
|
||||
nsILoadContextInfo *aLoadContextInfo,
|
||||
nsIApplicationCache **out)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return device->ChooseApplicationCache(key, aLoadContextInfo, out);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::CacheOpportunistically(nsIApplicationCache* cache,
|
||||
const nsACString &key)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->CacheOpportunistically(cache, key);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::Evict(nsILoadContextInfo *aInfo)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->Evict(aInfo);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::EvictMatchingOriginAttributes(nsAString const &aPattern)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozilla::OriginAttributesPattern pattern;
|
||||
if (!pattern.Init(aPattern)) {
|
||||
NS_ERROR("Could not parse OriginAttributesPattern JSON in clear-origin-attributes-data notification");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return device->Evict(pattern);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::GetGroups(uint32_t *count,
|
||||
char ***keys)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->GetGroups(count, keys);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsApplicationCacheService::GetGroupsTimeOrdered(uint32_t *count,
|
||||
char ***keys)
|
||||
{
|
||||
if (!mCacheService)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
RefPtr<nsOfflineCacheDevice> device;
|
||||
nsresult rv = mCacheService->GetOfflineDevice(getter_AddRefs(device));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return device->GetGroupsTimeOrdered(count, keys);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AppCacheClearDataObserver: handles clearing appcache data for app uninstall
|
||||
// and clearing user data events.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
class AppCacheClearDataObserver final : public nsIObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIObserver implementation.
|
||||
NS_IMETHOD
|
||||
Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override
|
||||
{
|
||||
MOZ_ASSERT(!nsCRT::strcmp(aTopic, "clear-origin-attributes-data"));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIApplicationCacheService> cacheService =
|
||||
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return cacheService->EvictMatchingOriginAttributes(nsDependentString(aData));
|
||||
}
|
||||
|
||||
private:
|
||||
~AppCacheClearDataObserver() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(AppCacheClearDataObserver, nsIObserver)
|
||||
|
||||
} // namespace
|
||||
|
||||
// Instantiates and registers AppCacheClearDataObserver for notifications
|
||||
void
|
||||
nsApplicationCacheService::AppClearDataObserverInit()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
||||
if (observerService) {
|
||||
RefPtr<AppCacheClearDataObserver> obs = new AppCacheClearDataObserver();
|
||||
observerService->AddObserver(obs, "clear-origin-attributes-data", /*ownsWeak=*/ false);
|
||||
}
|
||||
}
|
||||
28
netwerk/cache/nsApplicationCacheService.h
vendored
Normal file
28
netwerk/cache/nsApplicationCacheService.h
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsApplicationCacheService_h_
|
||||
#define _nsApplicationCacheService_h_
|
||||
|
||||
#include "nsIApplicationCacheService.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsCacheService;
|
||||
|
||||
class nsApplicationCacheService final : public nsIApplicationCacheService
|
||||
{
|
||||
public:
|
||||
nsApplicationCacheService();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIAPPLICATIONCACHESERVICE
|
||||
|
||||
static void AppClearDataObserverInit();
|
||||
|
||||
private:
|
||||
~nsApplicationCacheService();
|
||||
RefPtr<nsCacheService> mCacheService;
|
||||
};
|
||||
|
||||
#endif // _nsApplicationCacheService_h_
|
||||
95
netwerk/cache/nsCache.cpp
vendored
Normal file
95
netwerk/cache/nsCache.cpp
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCache.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsDependentSubstring.h"
|
||||
#include "nsString.h"
|
||||
|
||||
|
||||
/**
|
||||
* Cache Service Utility Functions
|
||||
*/
|
||||
|
||||
mozilla::LazyLogModule gCacheLog("cache");
|
||||
|
||||
void
|
||||
CacheLogPrintPath(mozilla::LogLevel level, const char * format, nsIFile * item)
|
||||
{
|
||||
nsAutoCString path;
|
||||
nsresult rv = item->GetNativePath(path);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_LOG(gCacheLog, level, (format, path.get()));
|
||||
} else {
|
||||
MOZ_LOG(gCacheLog, level, ("GetNativePath failed: %x", rv));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
SecondsFromPRTime(PRTime prTime)
|
||||
{
|
||||
int64_t microSecondsPerSecond = PR_USEC_PER_SEC;
|
||||
return uint32_t(prTime / microSecondsPerSecond);
|
||||
}
|
||||
|
||||
|
||||
PRTime
|
||||
PRTimeFromSeconds(uint32_t seconds)
|
||||
{
|
||||
int64_t intermediateResult = seconds;
|
||||
PRTime prTime = intermediateResult * PR_USEC_PER_SEC;
|
||||
return prTime;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
ClientIDFromCacheKey(const nsACString& key, char ** result)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
*result = nullptr;
|
||||
|
||||
nsReadingIterator<char> colon;
|
||||
key.BeginReading(colon);
|
||||
|
||||
nsReadingIterator<char> start;
|
||||
key.BeginReading(start);
|
||||
|
||||
nsReadingIterator<char> end;
|
||||
key.EndReading(end);
|
||||
|
||||
if (FindCharInReadable(':', colon, end)) {
|
||||
*result = ToNewCString( Substring(start, colon));
|
||||
if (!*result) rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
} else {
|
||||
NS_ASSERTION(false, "FindCharInRead failed to find ':'");
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
ClientKeyFromCacheKey(const nsCString& key, nsACString &result)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
nsReadingIterator<char> start;
|
||||
key.BeginReading(start);
|
||||
|
||||
nsReadingIterator<char> end;
|
||||
key.EndReading(end);
|
||||
|
||||
if (FindCharInReadable(':', start, end)) {
|
||||
++start; // advance past clientID ':' delimiter
|
||||
result.Assign(Substring(start, end));
|
||||
} else {
|
||||
NS_ASSERTION(false, "FindCharInRead failed to find ':'");
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
result.Truncate(0);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
42
netwerk/cache/nsCache.h
vendored
Normal file
42
netwerk/cache/nsCache.h
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Cache Service Utility Functions
|
||||
*/
|
||||
|
||||
#ifndef _nsCache_h_
|
||||
#define _nsCache_h_
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsAString.h"
|
||||
#include "prtime.h"
|
||||
#include "nsError.h"
|
||||
|
||||
// PR_LOG args = "format string", arg, arg, ...
|
||||
extern mozilla::LazyLogModule gCacheLog;
|
||||
void CacheLogPrintPath(mozilla::LogLevel level,
|
||||
const char * format,
|
||||
nsIFile * item);
|
||||
#define CACHE_LOG_INFO(args) MOZ_LOG(gCacheLog, mozilla::LogLevel::Info, args)
|
||||
#define CACHE_LOG_ERROR(args) MOZ_LOG(gCacheLog, mozilla::LogLevel::Error, args)
|
||||
#define CACHE_LOG_WARNING(args) MOZ_LOG(gCacheLog, mozilla::LogLevel::Warning, args)
|
||||
#define CACHE_LOG_DEBUG(args) MOZ_LOG(gCacheLog, mozilla::LogLevel::Debug, args)
|
||||
#define CACHE_LOG_PATH(level, format, item) \
|
||||
CacheLogPrintPath(level, format, item)
|
||||
|
||||
|
||||
extern uint32_t SecondsFromPRTime(PRTime prTime);
|
||||
extern PRTime PRTimeFromSeconds(uint32_t seconds);
|
||||
|
||||
|
||||
extern nsresult ClientIDFromCacheKey(const nsACString& key, char ** result);
|
||||
extern nsresult ClientKeyFromCacheKey(const nsCString& key, nsACString &result);
|
||||
|
||||
|
||||
#endif // _nsCache_h
|
||||
63
netwerk/cache/nsCacheDevice.h
vendored
Normal file
63
netwerk/cache/nsCacheDevice.h
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCacheDevice_h_
|
||||
#define _nsCacheDevice_h_
|
||||
|
||||
#include "nspr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsICache.h"
|
||||
|
||||
class nsIFile;
|
||||
class nsCString;
|
||||
class nsCacheEntry;
|
||||
class nsICacheVisitor;
|
||||
class nsIInputStream;
|
||||
class nsIOutputStream;
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheDevice
|
||||
*******************************************************************************/
|
||||
class nsCacheDevice {
|
||||
public:
|
||||
nsCacheDevice() { MOZ_COUNT_CTOR(nsCacheDevice); }
|
||||
virtual ~nsCacheDevice() { MOZ_COUNT_DTOR(nsCacheDevice); }
|
||||
|
||||
virtual nsresult Init() = 0;
|
||||
virtual nsresult Shutdown() = 0;
|
||||
|
||||
virtual const char * GetDeviceID(void) = 0;
|
||||
virtual nsCacheEntry * FindEntry( nsCString * key, bool *collision ) = 0;
|
||||
|
||||
virtual nsresult DeactivateEntry( nsCacheEntry * entry ) = 0;
|
||||
virtual nsresult BindEntry( nsCacheEntry * entry ) = 0;
|
||||
virtual void DoomEntry( nsCacheEntry * entry ) = 0;
|
||||
|
||||
virtual nsresult OpenInputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIInputStream ** result) = 0;
|
||||
|
||||
virtual nsresult OpenOutputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIOutputStream ** result) = 0;
|
||||
|
||||
virtual nsresult GetFileForEntry( nsCacheEntry * entry,
|
||||
nsIFile ** result ) = 0;
|
||||
|
||||
virtual nsresult OnDataSizeChange( nsCacheEntry * entry, int32_t deltaSize ) = 0;
|
||||
|
||||
virtual nsresult Visit(nsICacheVisitor * visitor) = 0;
|
||||
|
||||
/**
|
||||
* Device must evict entries associated with clientID. If clientID == nullptr, all
|
||||
* entries must be evicted. Active entries must be doomed, rather than evicted.
|
||||
*/
|
||||
virtual nsresult EvictEntries(const char * clientID) = 0;
|
||||
};
|
||||
|
||||
#endif // _nsCacheDevice_h_
|
||||
508
netwerk/cache/nsCacheEntry.cpp
vendored
Normal file
508
netwerk/cache/nsCacheEntry.cpp
vendored
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "nsCache.h"
|
||||
#include "nspr.h"
|
||||
#include "nsCacheEntry.h"
|
||||
#include "nsCacheEntryDescriptor.h"
|
||||
#include "nsCacheMetaData.h"
|
||||
#include "nsCacheRequest.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsICacheService.h"
|
||||
#include "nsCacheService.h"
|
||||
#include "nsCacheDevice.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsCacheEntry::nsCacheEntry(const nsACString & key,
|
||||
bool streamBased,
|
||||
nsCacheStoragePolicy storagePolicy)
|
||||
: mKey(key),
|
||||
mFetchCount(0),
|
||||
mLastFetched(0),
|
||||
mLastModified(0),
|
||||
mExpirationTime(nsICache::NO_EXPIRATION_TIME),
|
||||
mFlags(0),
|
||||
mPredictedDataSize(-1),
|
||||
mDataSize(0),
|
||||
mCacheDevice(nullptr),
|
||||
mCustomDevice(nullptr),
|
||||
mData(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsCacheEntry);
|
||||
PR_INIT_CLIST(this);
|
||||
PR_INIT_CLIST(&mRequestQ);
|
||||
PR_INIT_CLIST(&mDescriptorQ);
|
||||
|
||||
if (streamBased) MarkStreamBased();
|
||||
SetStoragePolicy(storagePolicy);
|
||||
|
||||
MarkPublic();
|
||||
}
|
||||
|
||||
|
||||
nsCacheEntry::~nsCacheEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsCacheEntry);
|
||||
|
||||
if (mData)
|
||||
nsCacheService::ReleaseObject_Locked(mData, mThread);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsCacheEntry::Create( const char * key,
|
||||
bool streamBased,
|
||||
nsCacheStoragePolicy storagePolicy,
|
||||
nsCacheDevice * device,
|
||||
nsCacheEntry ** result)
|
||||
{
|
||||
nsCacheEntry* entry = new nsCacheEntry(nsCString(key),
|
||||
streamBased,
|
||||
storagePolicy);
|
||||
entry->SetCacheDevice(device);
|
||||
*result = entry;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntry::Fetched()
|
||||
{
|
||||
mLastFetched = SecondsFromPRTime(PR_Now());
|
||||
++mFetchCount;
|
||||
MarkEntryDirty();
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
nsCacheEntry::GetDeviceID()
|
||||
{
|
||||
if (mCacheDevice) return mCacheDevice->GetDeviceID();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntry::TouchData()
|
||||
{
|
||||
mLastModified = SecondsFromPRTime(PR_Now());
|
||||
MarkDataDirty();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntry::SetData(nsISupports * data)
|
||||
{
|
||||
if (mData) {
|
||||
nsCacheService::ReleaseObject_Locked(mData, mThread);
|
||||
mData = nullptr;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
NS_ADDREF(mData = data);
|
||||
mThread = do_GetCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntry::TouchMetaData()
|
||||
{
|
||||
mLastModified = SecondsFromPRTime(PR_Now());
|
||||
MarkMetaDataDirty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cache entry states
|
||||
* 0 descriptors (new entry)
|
||||
* 0 descriptors (existing, bound entry)
|
||||
* n descriptors (existing, bound entry) valid
|
||||
* n descriptors (existing, bound entry) not valid (wait until valid or doomed)
|
||||
*/
|
||||
|
||||
nsresult
|
||||
nsCacheEntry::RequestAccess(nsCacheRequest * request, nsCacheAccessMode *accessGranted)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED;
|
||||
|
||||
if (!IsInitialized()) {
|
||||
// brand new, unbound entry
|
||||
if (request->IsStreamBased()) MarkStreamBased();
|
||||
MarkInitialized();
|
||||
|
||||
*accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE;
|
||||
NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request");
|
||||
PR_APPEND_LINK(request, &mRequestQ);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (IsStreamData() != request->IsStreamBased()) {
|
||||
*accessGranted = nsICache::ACCESS_NONE;
|
||||
return request->IsStreamBased() ?
|
||||
NS_ERROR_CACHE_DATA_IS_NOT_STREAM : NS_ERROR_CACHE_DATA_IS_STREAM;
|
||||
}
|
||||
|
||||
if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) {
|
||||
// 1st descriptor for existing bound entry
|
||||
*accessGranted = request->AccessRequested();
|
||||
if (*accessGranted & nsICache::ACCESS_WRITE) {
|
||||
MarkInvalid();
|
||||
} else {
|
||||
MarkValid();
|
||||
}
|
||||
} else {
|
||||
// nth request for existing, bound entry
|
||||
*accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE;
|
||||
if (!IsValid())
|
||||
rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION;
|
||||
}
|
||||
PR_APPEND_LINK(request,&mRequestQ);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsCacheEntry::CreateDescriptor(nsCacheRequest * request,
|
||||
nsCacheAccessMode accessGranted,
|
||||
nsICacheEntryDescriptor ** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(request && result);
|
||||
|
||||
nsCacheEntryDescriptor * descriptor =
|
||||
new nsCacheEntryDescriptor(this, accessGranted);
|
||||
|
||||
// XXX check request is on q
|
||||
PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success
|
||||
|
||||
if (descriptor == nullptr)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
PR_APPEND_LINK(descriptor, &mDescriptorQ);
|
||||
|
||||
CACHE_LOG_DEBUG((" descriptor %p created for request %p on entry %p\n",
|
||||
descriptor, request, this));
|
||||
|
||||
NS_ADDREF(*result = descriptor);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsCacheEntry::RemoveRequest(nsCacheRequest * request)
|
||||
{
|
||||
// XXX if debug: verify this request belongs to this entry
|
||||
PR_REMOVE_AND_INIT_LINK(request);
|
||||
|
||||
// return true if this entry should stay active
|
||||
return !((PR_CLIST_IS_EMPTY(&mRequestQ)) &&
|
||||
(PR_CLIST_IS_EMPTY(&mDescriptorQ)));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor,
|
||||
bool * doomEntry)
|
||||
{
|
||||
NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!");
|
||||
|
||||
*doomEntry = descriptor->ClearCacheEntry();
|
||||
|
||||
PR_REMOVE_AND_INIT_LINK(descriptor);
|
||||
|
||||
if (!PR_CLIST_IS_EMPTY(&mDescriptorQ))
|
||||
return true; // stay active if we still have open descriptors
|
||||
|
||||
if (PR_CLIST_IS_EMPTY(&mRequestQ))
|
||||
return false; // no descriptors or requests, we can deactivate
|
||||
|
||||
return true; // find next best request to give a descriptor to
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntry::DetachDescriptors()
|
||||
{
|
||||
nsCacheEntryDescriptor * descriptor =
|
||||
(nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
|
||||
|
||||
while (descriptor != &mDescriptorQ) {
|
||||
nsCacheEntryDescriptor * nextDescriptor =
|
||||
(nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
|
||||
|
||||
descriptor->ClearCacheEntry();
|
||||
PR_REMOVE_AND_INIT_LINK(descriptor);
|
||||
descriptor = nextDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntry::GetDescriptors(
|
||||
nsTArray<RefPtr<nsCacheEntryDescriptor> > &outDescriptors)
|
||||
{
|
||||
nsCacheEntryDescriptor * descriptor =
|
||||
(nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
|
||||
|
||||
while (descriptor != &mDescriptorQ) {
|
||||
nsCacheEntryDescriptor * nextDescriptor =
|
||||
(nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
|
||||
|
||||
outDescriptors.AppendElement(descriptor);
|
||||
descriptor = nextDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheEntryInfo - for implementing about:cache
|
||||
*****************************************************************************/
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCacheEntryInfo, nsICacheEntryInfo)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetClientID(char ** clientID)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(clientID);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
return ClientIDFromCacheKey(*mCacheEntry->Key(), clientID);
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetDeviceID(char ** deviceID)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(deviceID);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*deviceID = NS_strdup(mCacheEntry->GetDeviceID());
|
||||
return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetKey(nsACString &key)
|
||||
{
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
return ClientKeyFromCacheKey(*mCacheEntry->Key(), key);
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetFetchCount(int32_t * fetchCount)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(fetchCount);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*fetchCount = mCacheEntry->FetchCount();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetLastFetched(uint32_t * lastFetched)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(lastFetched);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*lastFetched = mCacheEntry->LastFetched();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetLastModified(uint32_t * lastModified)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(lastModified);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*lastModified = mCacheEntry->LastModified();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetExpirationTime(uint32_t * expirationTime)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(expirationTime);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*expirationTime = mCacheEntry->ExpirationTime();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::GetDataSize(uint32_t * dataSize)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(dataSize);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*dataSize = mCacheEntry->DataSize();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheEntryInfo::IsStreamBased(bool * result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*result = mCacheEntry->IsStreamData();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheEntryHashTable
|
||||
*****************************************************************************/
|
||||
|
||||
const PLDHashTableOps
|
||||
nsCacheEntryHashTable::ops =
|
||||
{
|
||||
HashKey,
|
||||
MatchEntry,
|
||||
MoveEntry,
|
||||
ClearEntry
|
||||
};
|
||||
|
||||
|
||||
nsCacheEntryHashTable::nsCacheEntryHashTable()
|
||||
: table(&ops, sizeof(nsCacheEntryHashTableEntry), kInitialTableLength)
|
||||
, initialized(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsCacheEntryHashTable);
|
||||
}
|
||||
|
||||
|
||||
nsCacheEntryHashTable::~nsCacheEntryHashTable()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsCacheEntryHashTable);
|
||||
if (initialized)
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntryHashTable::Init()
|
||||
{
|
||||
table.ClearAndPrepareForLength(kInitialTableLength);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsCacheEntryHashTable::Shutdown()
|
||||
{
|
||||
if (initialized) {
|
||||
table.ClearAndPrepareForLength(kInitialTableLength);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsCacheEntry *
|
||||
nsCacheEntryHashTable::GetEntry( const nsCString * key)
|
||||
{
|
||||
NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
|
||||
if (!initialized) return nullptr;
|
||||
|
||||
PLDHashEntryHdr *hashEntry = table.Search(key);
|
||||
return hashEntry ? ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsCacheEntryHashTable::AddEntry( nsCacheEntry *cacheEntry)
|
||||
{
|
||||
PLDHashEntryHdr *hashEntry;
|
||||
|
||||
NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
|
||||
if (!initialized) return NS_ERROR_NOT_INITIALIZED;
|
||||
if (!cacheEntry) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
hashEntry = table.Add(&(cacheEntry->mKey), fallible);
|
||||
|
||||
if (!hashEntry)
|
||||
return NS_ERROR_FAILURE;
|
||||
NS_ASSERTION(((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry == 0,
|
||||
"### nsCacheEntryHashTable::AddEntry - entry already used");
|
||||
((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = cacheEntry;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntryHashTable::RemoveEntry( nsCacheEntry *cacheEntry)
|
||||
{
|
||||
NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
|
||||
NS_ASSERTION(cacheEntry, "### cacheEntry == nullptr");
|
||||
|
||||
if (!initialized) return; // NS_ERROR_NOT_INITIALIZED
|
||||
|
||||
#if DEBUG
|
||||
// XXX debug code to make sure we have the entry we're trying to remove
|
||||
nsCacheEntry *check = GetEntry(&(cacheEntry->mKey));
|
||||
NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!");
|
||||
#endif
|
||||
table.Remove(&(cacheEntry->mKey));
|
||||
}
|
||||
|
||||
PLDHashTable::Iterator
|
||||
nsCacheEntryHashTable::Iter()
|
||||
{
|
||||
return PLDHashTable::Iterator(&table);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash table operation callback functions
|
||||
*/
|
||||
|
||||
PLDHashNumber
|
||||
nsCacheEntryHashTable::HashKey(const void *key)
|
||||
{
|
||||
return HashString(*static_cast<const nsCString *>(key));
|
||||
}
|
||||
|
||||
bool
|
||||
nsCacheEntryHashTable::MatchEntry(const PLDHashEntryHdr * hashEntry,
|
||||
const void * key)
|
||||
{
|
||||
NS_ASSERTION(key != nullptr, "### nsCacheEntryHashTable::MatchEntry : null key");
|
||||
nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
|
||||
|
||||
return cacheEntry->mKey.Equals(*(nsCString *)key);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */,
|
||||
const PLDHashEntryHdr *from,
|
||||
PLDHashEntryHdr *to)
|
||||
{
|
||||
((nsCacheEntryHashTableEntry *)to)->cacheEntry =
|
||||
((nsCacheEntryHashTableEntry *)from)->cacheEntry;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */,
|
||||
PLDHashEntryHdr * hashEntry)
|
||||
{
|
||||
((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = 0;
|
||||
}
|
||||
302
netwerk/cache/nsCacheEntry.h
vendored
Normal file
302
netwerk/cache/nsCacheEntry.h
vendored
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCacheEntry_h_
|
||||
#define _nsCacheEntry_h_
|
||||
|
||||
#include "nsICache.h"
|
||||
#include "nsICacheEntryDescriptor.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsCacheMetaData.h"
|
||||
|
||||
#include "nspr.h"
|
||||
#include "PLDHashTable.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nscore.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsAString.h"
|
||||
|
||||
class nsCacheDevice;
|
||||
class nsCacheMetaData;
|
||||
class nsCacheRequest;
|
||||
class nsCacheEntryDescriptor;
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheEntry
|
||||
*******************************************************************************/
|
||||
class nsCacheEntry : public PRCList
|
||||
{
|
||||
public:
|
||||
|
||||
nsCacheEntry(const nsACString & key,
|
||||
bool streamBased,
|
||||
nsCacheStoragePolicy storagePolicy);
|
||||
~nsCacheEntry();
|
||||
|
||||
|
||||
static nsresult Create( const char * key,
|
||||
bool streamBased,
|
||||
nsCacheStoragePolicy storagePolicy,
|
||||
nsCacheDevice * device,
|
||||
nsCacheEntry ** result);
|
||||
|
||||
nsCString * Key() { return &mKey; }
|
||||
|
||||
int32_t FetchCount() { return mFetchCount; }
|
||||
void SetFetchCount( int32_t count) { mFetchCount = count; }
|
||||
void Fetched();
|
||||
|
||||
uint32_t LastFetched() { return mLastFetched; }
|
||||
void SetLastFetched( uint32_t lastFetched) { mLastFetched = lastFetched; }
|
||||
|
||||
uint32_t LastModified() { return mLastModified; }
|
||||
void SetLastModified( uint32_t lastModified) { mLastModified = lastModified; }
|
||||
|
||||
uint32_t ExpirationTime() { return mExpirationTime; }
|
||||
void SetExpirationTime( uint32_t expires) { mExpirationTime = expires; }
|
||||
|
||||
uint32_t Size()
|
||||
{ return mDataSize + mMetaData.Size() + mKey.Length() ; }
|
||||
|
||||
nsCacheDevice * CacheDevice() { return mCacheDevice; }
|
||||
void SetCacheDevice( nsCacheDevice * device) { mCacheDevice = device; }
|
||||
void SetCustomCacheDevice( nsCacheDevice * device )
|
||||
{ mCustomDevice = device; }
|
||||
nsCacheDevice * CustomCacheDevice() { return mCustomDevice; }
|
||||
const char * GetDeviceID();
|
||||
|
||||
/**
|
||||
* Data accessors
|
||||
*/
|
||||
nsISupports *Data() { return mData; }
|
||||
void SetData( nsISupports * data);
|
||||
|
||||
int64_t PredictedDataSize() { return mPredictedDataSize; }
|
||||
void SetPredictedDataSize(int64_t size) { mPredictedDataSize = size; }
|
||||
|
||||
uint32_t DataSize() { return mDataSize; }
|
||||
void SetDataSize( uint32_t size) { mDataSize = size; }
|
||||
|
||||
void TouchData();
|
||||
|
||||
/**
|
||||
* Meta data accessors
|
||||
*/
|
||||
const char * GetMetaDataElement( const char * key) { return mMetaData.GetElement(key); }
|
||||
nsresult SetMetaDataElement( const char * key,
|
||||
const char * value) { return mMetaData.SetElement(key, value); }
|
||||
nsresult VisitMetaDataElements( nsICacheMetaDataVisitor * visitor) { return mMetaData.VisitElements(visitor); }
|
||||
nsresult FlattenMetaData(char * buffer, uint32_t bufSize) { return mMetaData.FlattenMetaData(buffer, bufSize); }
|
||||
nsresult UnflattenMetaData(const char * buffer, uint32_t bufSize) { return mMetaData.UnflattenMetaData(buffer, bufSize); }
|
||||
uint32_t MetaDataSize() { return mMetaData.Size(); }
|
||||
|
||||
void TouchMetaData();
|
||||
|
||||
|
||||
/**
|
||||
* Security Info accessors
|
||||
*/
|
||||
nsISupports* SecurityInfo() { return mSecurityInfo; }
|
||||
void SetSecurityInfo( nsISupports * info) { mSecurityInfo = info; }
|
||||
|
||||
|
||||
// XXX enumerate MetaData method
|
||||
|
||||
|
||||
enum CacheEntryFlags {
|
||||
eStoragePolicyMask = 0x000000FF,
|
||||
eDoomedMask = 0x00000100,
|
||||
eEntryDirtyMask = 0x00000200,
|
||||
eDataDirtyMask = 0x00000400,
|
||||
eMetaDataDirtyMask = 0x00000800,
|
||||
eStreamDataMask = 0x00001000,
|
||||
eActiveMask = 0x00002000,
|
||||
eInitializedMask = 0x00004000,
|
||||
eValidMask = 0x00008000,
|
||||
eBindingMask = 0x00010000,
|
||||
ePrivateMask = 0x00020000
|
||||
};
|
||||
|
||||
void MarkBinding() { mFlags |= eBindingMask; }
|
||||
void ClearBinding() { mFlags &= ~eBindingMask; }
|
||||
bool IsBinding() { return (mFlags & eBindingMask) != 0; }
|
||||
|
||||
void MarkEntryDirty() { mFlags |= eEntryDirtyMask; }
|
||||
void MarkEntryClean() { mFlags &= ~eEntryDirtyMask; }
|
||||
void MarkDataDirty() { mFlags |= eDataDirtyMask; }
|
||||
void MarkDataClean() { mFlags &= ~eDataDirtyMask; }
|
||||
void MarkMetaDataDirty() { mFlags |= eMetaDataDirtyMask; }
|
||||
void MarkMetaDataClean() { mFlags &= ~eMetaDataDirtyMask; }
|
||||
void MarkStreamData() { mFlags |= eStreamDataMask; }
|
||||
void MarkValid() { mFlags |= eValidMask; }
|
||||
void MarkInvalid() { mFlags &= ~eValidMask; }
|
||||
void MarkPrivate() { mFlags |= ePrivateMask; }
|
||||
void MarkPublic() { mFlags &= ~ePrivateMask; }
|
||||
// void MarkAllowedInMemory() { mFlags |= eAllowedInMemoryMask; }
|
||||
// void MarkAllowedOnDisk() { mFlags |= eAllowedOnDiskMask; }
|
||||
|
||||
bool IsDoomed() { return (mFlags & eDoomedMask) != 0; }
|
||||
bool IsEntryDirty() { return (mFlags & eEntryDirtyMask) != 0; }
|
||||
bool IsDataDirty() { return (mFlags & eDataDirtyMask) != 0; }
|
||||
bool IsMetaDataDirty() { return (mFlags & eMetaDataDirtyMask) != 0; }
|
||||
bool IsStreamData() { return (mFlags & eStreamDataMask) != 0; }
|
||||
bool IsActive() { return (mFlags & eActiveMask) != 0; }
|
||||
bool IsInitialized() { return (mFlags & eInitializedMask) != 0; }
|
||||
bool IsValid() { return (mFlags & eValidMask) != 0; }
|
||||
bool IsInvalid() { return (mFlags & eValidMask) == 0; }
|
||||
bool IsInUse() { return IsBinding() ||
|
||||
!(PR_CLIST_IS_EMPTY(&mRequestQ) &&
|
||||
PR_CLIST_IS_EMPTY(&mDescriptorQ)); }
|
||||
bool IsNotInUse() { return !IsInUse(); }
|
||||
bool IsPrivate() { return (mFlags & ePrivateMask) != 0; }
|
||||
|
||||
|
||||
bool IsAllowedInMemory()
|
||||
{
|
||||
return (StoragePolicy() == nsICache::STORE_ANYWHERE) ||
|
||||
(StoragePolicy() == nsICache::STORE_IN_MEMORY);
|
||||
}
|
||||
|
||||
bool IsAllowedOnDisk()
|
||||
{
|
||||
return !IsPrivate() && ((StoragePolicy() == nsICache::STORE_ANYWHERE) ||
|
||||
(StoragePolicy() == nsICache::STORE_ON_DISK));
|
||||
}
|
||||
|
||||
bool IsAllowedOffline()
|
||||
{
|
||||
return (StoragePolicy() == nsICache::STORE_OFFLINE);
|
||||
}
|
||||
|
||||
nsCacheStoragePolicy StoragePolicy()
|
||||
{
|
||||
return (nsCacheStoragePolicy)(mFlags & eStoragePolicyMask);
|
||||
}
|
||||
|
||||
void SetStoragePolicy(nsCacheStoragePolicy policy)
|
||||
{
|
||||
NS_ASSERTION(policy <= 0xFF, "too many bits in nsCacheStoragePolicy");
|
||||
mFlags &= ~eStoragePolicyMask; // clear storage policy bits
|
||||
mFlags |= policy;
|
||||
}
|
||||
|
||||
|
||||
// methods for nsCacheService
|
||||
nsresult RequestAccess( nsCacheRequest * request, nsCacheAccessMode *accessGranted);
|
||||
nsresult CreateDescriptor( nsCacheRequest * request,
|
||||
nsCacheAccessMode accessGranted,
|
||||
nsICacheEntryDescriptor ** result);
|
||||
|
||||
bool RemoveRequest( nsCacheRequest * request);
|
||||
bool RemoveDescriptor( nsCacheEntryDescriptor * descriptor,
|
||||
bool * doomEntry);
|
||||
|
||||
void GetDescriptors(nsTArray<RefPtr<nsCacheEntryDescriptor> > &outDescriptors);
|
||||
|
||||
private:
|
||||
friend class nsCacheEntryHashTable;
|
||||
friend class nsCacheService;
|
||||
|
||||
void DetachDescriptors();
|
||||
|
||||
// internal methods
|
||||
void MarkDoomed() { mFlags |= eDoomedMask; }
|
||||
void MarkStreamBased() { mFlags |= eStreamDataMask; }
|
||||
void MarkInitialized() { mFlags |= eInitializedMask; }
|
||||
void MarkActive() { mFlags |= eActiveMask; }
|
||||
void MarkInactive() { mFlags &= ~eActiveMask; }
|
||||
|
||||
nsCString mKey;
|
||||
uint32_t mFetchCount; // 4
|
||||
uint32_t mLastFetched; // 4
|
||||
uint32_t mLastModified; // 4
|
||||
uint32_t mLastValidated; // 4
|
||||
uint32_t mExpirationTime; // 4
|
||||
uint32_t mFlags; // 4
|
||||
int64_t mPredictedDataSize; // Size given by ContentLength.
|
||||
uint32_t mDataSize; // 4
|
||||
nsCacheDevice * mCacheDevice; // 4
|
||||
nsCacheDevice * mCustomDevice; // 4
|
||||
nsCOMPtr<nsISupports> mSecurityInfo; //
|
||||
nsISupports * mData; // strong ref
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
nsCacheMetaData mMetaData; // 4
|
||||
PRCList mRequestQ; // 8
|
||||
PRCList mDescriptorQ; // 8
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheEntryInfo
|
||||
*******************************************************************************/
|
||||
class nsCacheEntryInfo : public nsICacheEntryInfo {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICACHEENTRYINFO
|
||||
|
||||
explicit nsCacheEntryInfo(nsCacheEntry* entry)
|
||||
: mCacheEntry(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void DetachEntry() { mCacheEntry = nullptr; }
|
||||
|
||||
private:
|
||||
nsCacheEntry * mCacheEntry;
|
||||
|
||||
virtual ~nsCacheEntryInfo() {}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheEntryHashTable
|
||||
*******************************************************************************/
|
||||
|
||||
struct nsCacheEntryHashTableEntry : public PLDHashEntryHdr
|
||||
{
|
||||
nsCacheEntry *cacheEntry;
|
||||
};
|
||||
|
||||
class nsCacheEntryHashTable
|
||||
{
|
||||
public:
|
||||
nsCacheEntryHashTable();
|
||||
~nsCacheEntryHashTable();
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
nsCacheEntry *GetEntry( const nsCString * key);
|
||||
nsresult AddEntry( nsCacheEntry *entry);
|
||||
void RemoveEntry( nsCacheEntry *entry);
|
||||
|
||||
PLDHashTable::Iterator Iter();
|
||||
|
||||
private:
|
||||
// PLDHashTable operation callbacks
|
||||
static PLDHashNumber HashKey(const void *key);
|
||||
|
||||
static bool MatchEntry(const PLDHashEntryHdr * entry,
|
||||
const void * key);
|
||||
|
||||
static void MoveEntry( PLDHashTable *table,
|
||||
const PLDHashEntryHdr *from,
|
||||
PLDHashEntryHdr *to);
|
||||
|
||||
static void ClearEntry( PLDHashTable *table, PLDHashEntryHdr *entry);
|
||||
|
||||
static void Finalize( PLDHashTable *table);
|
||||
|
||||
// member variables
|
||||
static const PLDHashTableOps ops;
|
||||
PLDHashTable table;
|
||||
bool initialized;
|
||||
|
||||
static const uint32_t kInitialTableLength = 256;
|
||||
};
|
||||
|
||||
#endif // _nsCacheEntry_h_
|
||||
1475
netwerk/cache/nsCacheEntryDescriptor.cpp
vendored
Normal file
1475
netwerk/cache/nsCacheEntryDescriptor.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
237
netwerk/cache/nsCacheEntryDescriptor.h
vendored
Normal file
237
netwerk/cache/nsCacheEntryDescriptor.h
vendored
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#ifndef _nsCacheEntryDescriptor_h_
|
||||
#define _nsCacheEntryDescriptor_h_
|
||||
|
||||
#include "nsICacheEntryDescriptor.h"
|
||||
#include "nsCacheEntry.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsCacheService.h"
|
||||
#include "zlib.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheEntryDescriptor
|
||||
*******************************************************************************/
|
||||
class nsCacheEntryDescriptor final :
|
||||
public PRCList,
|
||||
public nsICacheEntryDescriptor
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICACHEENTRYDESCRIPTOR
|
||||
NS_DECL_NSICACHEENTRYINFO
|
||||
|
||||
friend class nsAsyncDoomEvent;
|
||||
friend class nsCacheService;
|
||||
|
||||
nsCacheEntryDescriptor(nsCacheEntry * entry, nsCacheAccessMode mode);
|
||||
|
||||
/**
|
||||
* utility method to attempt changing data size of associated entry
|
||||
*/
|
||||
nsresult RequestDataSizeChange(int32_t deltaSize);
|
||||
|
||||
/**
|
||||
* methods callbacks for nsCacheService
|
||||
*/
|
||||
nsCacheEntry * CacheEntry(void) { return mCacheEntry; }
|
||||
bool ClearCacheEntry(void)
|
||||
{
|
||||
NS_ASSERTION(mInputWrappers.IsEmpty(), "Bad state");
|
||||
NS_ASSERTION(!mOutputWrapper, "Bad state");
|
||||
|
||||
bool doomEntry = false;
|
||||
bool asyncDoomPending;
|
||||
{
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
asyncDoomPending = mAsyncDoomPending;
|
||||
}
|
||||
|
||||
if (asyncDoomPending && mCacheEntry) {
|
||||
doomEntry = true;
|
||||
mDoomedOnClose = true;
|
||||
}
|
||||
mCacheEntry = nullptr;
|
||||
|
||||
return doomEntry;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsCacheEntryDescriptor();
|
||||
|
||||
/*************************************************************************
|
||||
* input stream wrapper class -
|
||||
*
|
||||
* The input stream wrapper references the descriptor, but the descriptor
|
||||
* doesn't need any references to the stream wrapper.
|
||||
*************************************************************************/
|
||||
class nsInputStreamWrapper : public nsIInputStream {
|
||||
friend class nsCacheEntryDescriptor;
|
||||
|
||||
private:
|
||||
nsCacheEntryDescriptor * mDescriptor;
|
||||
nsCOMPtr<nsIInputStream> mInput;
|
||||
uint32_t mStartOffset;
|
||||
bool mInitialized;
|
||||
mozilla::Mutex mLock;
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
|
||||
nsInputStreamWrapper(nsCacheEntryDescriptor * desc, uint32_t off)
|
||||
: mDescriptor(desc)
|
||||
, mStartOffset(off)
|
||||
, mInitialized(false)
|
||||
, mLock("nsInputStreamWrapper.mLock")
|
||||
{
|
||||
NS_ADDREF(mDescriptor);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsInputStreamWrapper()
|
||||
{
|
||||
NS_IF_RELEASE(mDescriptor);
|
||||
}
|
||||
|
||||
nsresult LazyInit();
|
||||
nsresult EnsureInit();
|
||||
nsresult Read_Locked(char *buf, uint32_t count, uint32_t *countRead);
|
||||
nsresult Close_Locked();
|
||||
void CloseInternal();
|
||||
};
|
||||
|
||||
|
||||
class nsDecompressInputStreamWrapper : public nsInputStreamWrapper {
|
||||
private:
|
||||
unsigned char* mReadBuffer;
|
||||
uint32_t mReadBufferLen;
|
||||
z_stream mZstream;
|
||||
bool mStreamInitialized;
|
||||
bool mStreamEnded;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
nsDecompressInputStreamWrapper(nsCacheEntryDescriptor * desc,
|
||||
uint32_t off)
|
||||
: nsInputStreamWrapper(desc, off)
|
||||
, mReadBuffer(0)
|
||||
, mReadBufferLen(0)
|
||||
, mStreamInitialized(false)
|
||||
, mStreamEnded(false)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Read(char* buf, uint32_t count, uint32_t * result) override;
|
||||
NS_IMETHOD Close() override;
|
||||
private:
|
||||
virtual ~nsDecompressInputStreamWrapper()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
nsresult InitZstream();
|
||||
nsresult EndZstream();
|
||||
};
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* output stream wrapper class -
|
||||
*
|
||||
* The output stream wrapper references the descriptor, but the descriptor
|
||||
* doesn't need any references to the stream wrapper.
|
||||
*************************************************************************/
|
||||
class nsOutputStreamWrapper : public nsIOutputStream {
|
||||
friend class nsCacheEntryDescriptor;
|
||||
|
||||
protected:
|
||||
nsCacheEntryDescriptor * mDescriptor;
|
||||
nsCOMPtr<nsIOutputStream> mOutput;
|
||||
uint32_t mStartOffset;
|
||||
bool mInitialized;
|
||||
mozilla::Mutex mLock;
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOUTPUTSTREAM
|
||||
|
||||
nsOutputStreamWrapper(nsCacheEntryDescriptor * desc, uint32_t off)
|
||||
: mDescriptor(desc)
|
||||
, mStartOffset(off)
|
||||
, mInitialized(false)
|
||||
, mLock("nsOutputStreamWrapper.mLock")
|
||||
{
|
||||
NS_ADDREF(mDescriptor); // owning ref
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsOutputStreamWrapper()
|
||||
{
|
||||
Close();
|
||||
|
||||
NS_ASSERTION(!mOutput, "Bad state");
|
||||
NS_ASSERTION(!mDescriptor, "Bad state");
|
||||
}
|
||||
|
||||
nsresult LazyInit();
|
||||
nsresult EnsureInit();
|
||||
nsresult OnWrite(uint32_t count);
|
||||
nsresult Write_Locked(const char * buf,
|
||||
uint32_t count,
|
||||
uint32_t * result);
|
||||
nsresult Close_Locked();
|
||||
void CloseInternal();
|
||||
};
|
||||
|
||||
|
||||
class nsCompressOutputStreamWrapper : public nsOutputStreamWrapper {
|
||||
private:
|
||||
unsigned char* mWriteBuffer;
|
||||
uint32_t mWriteBufferLen;
|
||||
z_stream mZstream;
|
||||
bool mStreamInitialized;
|
||||
bool mStreamEnded;
|
||||
uint32_t mUncompressedCount;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
nsCompressOutputStreamWrapper(nsCacheEntryDescriptor * desc,
|
||||
uint32_t off)
|
||||
: nsOutputStreamWrapper(desc, off)
|
||||
, mWriteBuffer(0)
|
||||
, mWriteBufferLen(0)
|
||||
, mStreamInitialized(false)
|
||||
, mStreamEnded(false)
|
||||
, mUncompressedCount(0)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Write(const char* buf, uint32_t count, uint32_t * result) override;
|
||||
NS_IMETHOD Close() override;
|
||||
private:
|
||||
virtual ~nsCompressOutputStreamWrapper()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
nsresult InitZstream();
|
||||
nsresult WriteBuffer();
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* nsCacheEntryDescriptor data members
|
||||
*/
|
||||
nsCacheEntry * mCacheEntry; // we are a child of the entry
|
||||
nsCacheAccessMode mAccessGranted;
|
||||
nsTArray<nsInputStreamWrapper*> mInputWrappers;
|
||||
nsOutputStreamWrapper * mOutputWrapper;
|
||||
mozilla::Mutex mLock;
|
||||
bool mAsyncDoomPending;
|
||||
bool mDoomedOnClose;
|
||||
bool mClosingDescriptor;
|
||||
};
|
||||
|
||||
|
||||
#endif // _nsCacheEntryDescriptor_h_
|
||||
162
netwerk/cache/nsCacheMetaData.cpp
vendored
Normal file
162
netwerk/cache/nsCacheMetaData.cpp
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCacheMetaData.h"
|
||||
#include "nsICacheEntryDescriptor.h"
|
||||
|
||||
const char *
|
||||
nsCacheMetaData::GetElement(const char * key)
|
||||
{
|
||||
const char * data = mBuffer;
|
||||
const char * limit = mBuffer + mMetaSize;
|
||||
|
||||
while (data < limit) {
|
||||
// Point to the value part
|
||||
const char * value = data + strlen(data) + 1;
|
||||
MOZ_ASSERT(value < limit, "Cache Metadata corrupted");
|
||||
if (strcmp(data, key) == 0)
|
||||
return value;
|
||||
|
||||
// Skip value part
|
||||
data = value + strlen(value) + 1;
|
||||
}
|
||||
MOZ_ASSERT(data == limit, "Metadata corrupted");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsCacheMetaData::SetElement(const char * key,
|
||||
const char * value)
|
||||
{
|
||||
const uint32_t keySize = strlen(key) + 1;
|
||||
char * pos = (char *)GetElement(key);
|
||||
|
||||
if (!value) {
|
||||
// No value means remove the key/value pair completely, if existing
|
||||
if (pos) {
|
||||
uint32_t oldValueSize = strlen(pos) + 1;
|
||||
uint32_t offset = pos - mBuffer;
|
||||
uint32_t remainder = mMetaSize - (offset + oldValueSize);
|
||||
|
||||
memmove(pos - keySize, pos + oldValueSize, remainder);
|
||||
mMetaSize -= keySize + oldValueSize;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const uint32_t valueSize = strlen(value) + 1;
|
||||
uint32_t newSize = mMetaSize + valueSize;
|
||||
if (pos) {
|
||||
const uint32_t oldValueSize = strlen(pos) + 1;
|
||||
const uint32_t offset = pos - mBuffer;
|
||||
const uint32_t remainder = mMetaSize - (offset + oldValueSize);
|
||||
|
||||
// Update the value in place
|
||||
newSize -= oldValueSize;
|
||||
nsresult rv = EnsureBuffer(newSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Move the remainder to the right place
|
||||
pos = mBuffer + offset;
|
||||
memmove(pos + valueSize, pos + oldValueSize, remainder);
|
||||
} else {
|
||||
// allocate new meta data element
|
||||
newSize += keySize;
|
||||
nsresult rv = EnsureBuffer(newSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Add after last element
|
||||
pos = mBuffer + mMetaSize;
|
||||
memcpy(pos, key, keySize);
|
||||
pos += keySize;
|
||||
}
|
||||
|
||||
// Update value
|
||||
memcpy(pos, value, valueSize);
|
||||
mMetaSize = newSize;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCacheMetaData::FlattenMetaData(char * buffer, uint32_t bufSize)
|
||||
{
|
||||
if (mMetaSize > bufSize) {
|
||||
NS_ERROR("buffer size too small for meta data.");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(buffer, mBuffer, mMetaSize);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCacheMetaData::UnflattenMetaData(const char * data, uint32_t size)
|
||||
{
|
||||
if (data && size) {
|
||||
// Check if the metadata ends with a zero byte.
|
||||
if (data[size-1] != '\0') {
|
||||
NS_ERROR("Cache MetaData is not null terminated");
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
// Check that there are an even number of zero bytes
|
||||
// to match the pattern { key \0 value \0 }
|
||||
bool odd = false;
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
if (data[i] == '\0')
|
||||
odd = !odd;
|
||||
}
|
||||
if (odd) {
|
||||
NS_ERROR("Cache MetaData is malformed");
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
nsresult rv = EnsureBuffer(size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
memcpy(mBuffer, data, size);
|
||||
mMetaSize = size;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCacheMetaData::VisitElements(nsICacheMetaDataVisitor * visitor)
|
||||
{
|
||||
const char * data = mBuffer;
|
||||
const char * limit = mBuffer + mMetaSize;
|
||||
|
||||
while (data < limit) {
|
||||
const char * key = data;
|
||||
// Skip key part
|
||||
data += strlen(data) + 1;
|
||||
MOZ_ASSERT(data < limit, "Metadata corrupted");
|
||||
bool keepGoing;
|
||||
nsresult rv = visitor->VisitMetaDataElement(key, data, &keepGoing);
|
||||
if (NS_FAILED(rv) || !keepGoing)
|
||||
return NS_OK;
|
||||
|
||||
// Skip value part
|
||||
data += strlen(data) + 1;
|
||||
}
|
||||
MOZ_ASSERT(data == limit, "Metadata corrupted");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCacheMetaData::EnsureBuffer(uint32_t bufSize)
|
||||
{
|
||||
if (mBufferSize < bufSize) {
|
||||
char * buf = (char *)realloc(mBuffer, bufSize);
|
||||
if (!buf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mBuffer = buf;
|
||||
mBufferSize = bufSize;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
45
netwerk/cache/nsCacheMetaData.h
vendored
Normal file
45
netwerk/cache/nsCacheMetaData.h
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCacheMetaData_h_
|
||||
#define _nsCacheMetaData_h_
|
||||
|
||||
#include "nspr.h"
|
||||
#include "nscore.h"
|
||||
|
||||
class nsICacheMetaDataVisitor;
|
||||
|
||||
class nsCacheMetaData {
|
||||
public:
|
||||
nsCacheMetaData() : mBuffer(nullptr), mBufferSize(0), mMetaSize(0) { }
|
||||
|
||||
~nsCacheMetaData() {
|
||||
mBufferSize = mMetaSize = 0;
|
||||
free(mBuffer);
|
||||
mBuffer = nullptr;
|
||||
}
|
||||
|
||||
const char * GetElement(const char * key);
|
||||
|
||||
nsresult SetElement(const char * key, const char * value);
|
||||
|
||||
uint32_t Size(void) { return mMetaSize; }
|
||||
|
||||
nsresult FlattenMetaData(char * buffer, uint32_t bufSize);
|
||||
|
||||
nsresult UnflattenMetaData(const char * buffer, uint32_t bufSize);
|
||||
|
||||
nsresult VisitElements(nsICacheMetaDataVisitor * visitor);
|
||||
|
||||
private:
|
||||
nsresult EnsureBuffer(uint32_t size);
|
||||
|
||||
char * mBuffer;
|
||||
uint32_t mBufferSize;
|
||||
uint32_t mMetaSize;
|
||||
};
|
||||
|
||||
#endif // _nsCacheMetaData_h
|
||||
158
netwerk/cache/nsCacheRequest.h
vendored
Normal file
158
netwerk/cache/nsCacheRequest.h
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCacheRequest_h_
|
||||
#define _nsCacheRequest_h_
|
||||
|
||||
#include "nspr.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsICache.h"
|
||||
#include "nsICacheListener.h"
|
||||
#include "nsCacheSession.h"
|
||||
#include "nsCacheService.h"
|
||||
|
||||
|
||||
class nsCacheRequest : public PRCList
|
||||
{
|
||||
typedef mozilla::CondVar CondVar;
|
||||
typedef mozilla::MutexAutoLock MutexAutoLock;
|
||||
typedef mozilla::Mutex Mutex;
|
||||
|
||||
private:
|
||||
friend class nsCacheService;
|
||||
friend class nsCacheEntry;
|
||||
friend class nsProcessRequestEvent;
|
||||
|
||||
nsCacheRequest( const nsACString & key,
|
||||
nsICacheListener * listener,
|
||||
nsCacheAccessMode accessRequested,
|
||||
bool blockingMode,
|
||||
nsCacheSession * session)
|
||||
: mKey(key),
|
||||
mInfo(0),
|
||||
mListener(listener),
|
||||
mLock("nsCacheRequest.mLock"),
|
||||
mCondVar(mLock, "nsCacheRequest.mCondVar"),
|
||||
mProfileDir(session->ProfileDir())
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsCacheRequest);
|
||||
PR_INIT_CLIST(this);
|
||||
SetAccessRequested(accessRequested);
|
||||
SetStoragePolicy(session->StoragePolicy());
|
||||
if (session->IsStreamBased()) MarkStreamBased();
|
||||
if (session->WillDoomEntriesIfExpired()) MarkDoomEntriesIfExpired();
|
||||
if (session->IsPrivate()) MarkPrivate();
|
||||
if (blockingMode == nsICache::BLOCKING) MarkBlockingMode();
|
||||
MarkWaitingForValidation();
|
||||
NS_IF_ADDREF(mListener);
|
||||
}
|
||||
|
||||
~nsCacheRequest()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsCacheRequest);
|
||||
NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "request still on a list");
|
||||
|
||||
if (mListener)
|
||||
nsCacheService::ReleaseObject_Locked(mListener, mThread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Accessors
|
||||
*/
|
||||
enum CacheRequestInfo {
|
||||
eStoragePolicyMask = 0x000000FF,
|
||||
eStreamBasedMask = 0x00000100,
|
||||
ePrivateMask = 0x00000200,
|
||||
eDoomEntriesIfExpiredMask = 0x00001000,
|
||||
eBlockingModeMask = 0x00010000,
|
||||
eWaitingForValidationMask = 0x00100000,
|
||||
eAccessRequestedMask = 0xFF000000
|
||||
};
|
||||
|
||||
void SetAccessRequested(nsCacheAccessMode mode)
|
||||
{
|
||||
NS_ASSERTION(mode <= 0xFF, "too many bits in nsCacheAccessMode");
|
||||
mInfo &= ~eAccessRequestedMask;
|
||||
mInfo |= mode << 24;
|
||||
}
|
||||
|
||||
nsCacheAccessMode AccessRequested()
|
||||
{
|
||||
return (nsCacheAccessMode)((mInfo >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
void MarkStreamBased() { mInfo |= eStreamBasedMask; }
|
||||
bool IsStreamBased() { return (mInfo & eStreamBasedMask) != 0; }
|
||||
|
||||
|
||||
void MarkDoomEntriesIfExpired() { mInfo |= eDoomEntriesIfExpiredMask; }
|
||||
bool WillDoomEntriesIfExpired() { return (0 != (mInfo & eDoomEntriesIfExpiredMask)); }
|
||||
|
||||
void MarkBlockingMode() { mInfo |= eBlockingModeMask; }
|
||||
bool IsBlocking() { return (0 != (mInfo & eBlockingModeMask)); }
|
||||
bool IsNonBlocking() { return !(mInfo & eBlockingModeMask); }
|
||||
|
||||
void SetStoragePolicy(nsCacheStoragePolicy policy)
|
||||
{
|
||||
NS_ASSERTION(policy <= 0xFF, "too many bits in nsCacheStoragePolicy");
|
||||
mInfo &= ~eStoragePolicyMask; // clear storage policy bits
|
||||
mInfo |= policy; // or in new bits
|
||||
}
|
||||
|
||||
nsCacheStoragePolicy StoragePolicy()
|
||||
{
|
||||
return (nsCacheStoragePolicy)(mInfo & eStoragePolicyMask);
|
||||
}
|
||||
|
||||
void MarkPrivate() { mInfo |= ePrivateMask; }
|
||||
void MarkPublic() { mInfo &= ~ePrivateMask; }
|
||||
bool IsPrivate() { return (mInfo & ePrivateMask) != 0; }
|
||||
|
||||
void MarkWaitingForValidation() { mInfo |= eWaitingForValidationMask; }
|
||||
void DoneWaitingForValidation() { mInfo &= ~eWaitingForValidationMask; }
|
||||
bool WaitingForValidation()
|
||||
{
|
||||
return (mInfo & eWaitingForValidationMask) != 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WaitForValidation(void)
|
||||
{
|
||||
if (!WaitingForValidation()) { // flag already cleared
|
||||
MarkWaitingForValidation(); // set up for next time
|
||||
return NS_OK; // early exit;
|
||||
}
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
while (WaitingForValidation()) {
|
||||
mCondVar.Wait();
|
||||
}
|
||||
MarkWaitingForValidation(); // set up for next time
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void WakeUp(void) {
|
||||
DoneWaitingForValidation();
|
||||
MutexAutoLock lock(mLock);
|
||||
mCondVar.Notify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data members
|
||||
*/
|
||||
nsCString mKey;
|
||||
uint32_t mInfo;
|
||||
nsICacheListener * mListener; // strong ref
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
Mutex mLock;
|
||||
CondVar mCondVar;
|
||||
nsCOMPtr<nsIFile> mProfileDir;
|
||||
};
|
||||
|
||||
#endif // _nsCacheRequest_h_
|
||||
3209
netwerk/cache/nsCacheService.cpp
vendored
Normal file
3209
netwerk/cache/nsCacheService.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
392
netwerk/cache/nsCacheService.h
vendored
Normal file
392
netwerk/cache/nsCacheService.h
vendored
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCacheService_h_
|
||||
#define _nsCacheService_h_
|
||||
|
||||
#include "nsICacheService.h"
|
||||
#include "nsCacheSession.h"
|
||||
#include "nsCacheDevice.h"
|
||||
#include "nsCacheEntry.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsICacheListener.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
|
||||
#include "prthread.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
class nsCacheRequest;
|
||||
class nsCacheProfilePrefObserver;
|
||||
class nsDiskCacheDevice;
|
||||
class nsMemoryCacheDevice;
|
||||
class nsOfflineCacheDevice;
|
||||
class nsCacheServiceAutoLock;
|
||||
class nsITimer;
|
||||
class mozIStorageService;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsNotifyDoomListener
|
||||
*****************************************************************************/
|
||||
|
||||
class nsNotifyDoomListener : public mozilla::Runnable {
|
||||
public:
|
||||
nsNotifyDoomListener(nsICacheListener *listener,
|
||||
nsresult status)
|
||||
: mListener(listener) // transfers reference
|
||||
, mStatus(status)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mListener->OnCacheEntryDoomed(mStatus);
|
||||
NS_RELEASE(mListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsICacheListener *mListener;
|
||||
nsresult mStatus;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheService
|
||||
******************************************************************************/
|
||||
|
||||
class nsCacheService final : public nsICacheServiceInternal,
|
||||
public nsIMemoryReporter
|
||||
{
|
||||
virtual ~nsCacheService();
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICACHESERVICE
|
||||
NS_DECL_NSICACHESERVICEINTERNAL
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
||||
nsCacheService();
|
||||
|
||||
// Define a Create method to be used with a factory:
|
||||
static nsresult
|
||||
Create(nsISupports* outer, const nsIID& iid, void* *result);
|
||||
|
||||
|
||||
/**
|
||||
* Methods called by nsCacheSession
|
||||
*/
|
||||
static nsresult OpenCacheEntry(nsCacheSession * session,
|
||||
const nsACString & key,
|
||||
nsCacheAccessMode accessRequested,
|
||||
bool blockingMode,
|
||||
nsICacheListener * listener,
|
||||
nsICacheEntryDescriptor ** result);
|
||||
|
||||
static nsresult EvictEntriesForSession(nsCacheSession * session);
|
||||
|
||||
static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
|
||||
bool * result);
|
||||
|
||||
static nsresult DoomEntry(nsCacheSession *session,
|
||||
const nsACString &key,
|
||||
nsICacheListener *listener);
|
||||
|
||||
/**
|
||||
* Methods called by nsCacheEntryDescriptor
|
||||
*/
|
||||
|
||||
static void CloseDescriptor(nsCacheEntryDescriptor * descriptor);
|
||||
|
||||
static nsresult GetFileForEntry(nsCacheEntry * entry,
|
||||
nsIFile ** result);
|
||||
|
||||
static nsresult OpenInputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIInputStream ** result);
|
||||
|
||||
static nsresult OpenOutputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIOutputStream ** result);
|
||||
|
||||
static nsresult OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize);
|
||||
|
||||
static nsresult SetCacheElement(nsCacheEntry * entry, nsISupports * element);
|
||||
|
||||
static nsresult ValidateEntry(nsCacheEntry * entry);
|
||||
|
||||
static int32_t CacheCompressionLevel();
|
||||
|
||||
static bool GetClearingEntries();
|
||||
|
||||
static void GetCacheBaseDirectoty(nsIFile ** result);
|
||||
static void GetDiskCacheDirectory(nsIFile ** result);
|
||||
static void GetAppCacheDirectory(nsIFile ** result);
|
||||
|
||||
/**
|
||||
* Methods called by any cache classes
|
||||
*/
|
||||
|
||||
static
|
||||
nsCacheService * GlobalInstance() { return gService; }
|
||||
|
||||
static nsresult DoomEntry(nsCacheEntry * entry);
|
||||
|
||||
static bool IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy policy);
|
||||
|
||||
/**
|
||||
* Called by disk cache to notify us to use the new max smart size
|
||||
*/
|
||||
static void MarkStartingFresh();
|
||||
|
||||
/**
|
||||
* Methods called by nsApplicationCacheService
|
||||
*/
|
||||
|
||||
nsresult GetOfflineDevice(nsOfflineCacheDevice ** aDevice);
|
||||
|
||||
/**
|
||||
* Creates an offline cache device that works over a specific profile directory.
|
||||
* A tool to preload offline cache for profiles different from the current
|
||||
* application's profile directory.
|
||||
*/
|
||||
nsresult GetCustomOfflineDevice(nsIFile *aProfileDir,
|
||||
int32_t aQuota,
|
||||
nsOfflineCacheDevice **aDevice);
|
||||
|
||||
// This method may be called to release an object while the cache service
|
||||
// lock is being held. If a non-null target is specified and the target
|
||||
// does not correspond to the current thread, then the release will be
|
||||
// proxied to the specified target. Otherwise, the object will be added to
|
||||
// the list of objects to be released when the cache service is unlocked.
|
||||
static void ReleaseObject_Locked(nsISupports * object,
|
||||
nsIEventTarget * target = nullptr);
|
||||
|
||||
static nsresult DispatchToCacheIOThread(nsIRunnable* event);
|
||||
|
||||
// Calling this method will block the calling thread until all pending
|
||||
// events on the cache-io thread has finished. The calling thread must
|
||||
// hold the cache-lock
|
||||
static nsresult SyncWithCacheIOThread();
|
||||
|
||||
|
||||
/**
|
||||
* Methods called by nsCacheProfilePrefObserver
|
||||
*/
|
||||
static void OnProfileShutdown();
|
||||
static void OnProfileChanged();
|
||||
|
||||
static void SetDiskCacheEnabled(bool enabled);
|
||||
// Sets the disk cache capacity (in kilobytes)
|
||||
static void SetDiskCacheCapacity(int32_t capacity);
|
||||
// Set max size for a disk-cache entry (in KB). -1 disables limit up to
|
||||
// 1/8th of disk cache size
|
||||
static void SetDiskCacheMaxEntrySize(int32_t maxSize);
|
||||
// Set max size for a memory-cache entry (in kilobytes). -1 disables
|
||||
// limit up to 90% of memory cache size
|
||||
static void SetMemoryCacheMaxEntrySize(int32_t maxSize);
|
||||
|
||||
static void SetOfflineCacheEnabled(bool enabled);
|
||||
// Sets the offline cache capacity (in kilobytes)
|
||||
static void SetOfflineCacheCapacity(int32_t capacity);
|
||||
|
||||
static void SetMemoryCache();
|
||||
|
||||
static void SetCacheCompressionLevel(int32_t level);
|
||||
|
||||
// Starts smart cache size computation if disk device is available
|
||||
static nsresult SetDiskSmartSize();
|
||||
|
||||
static void MoveOrRemoveDiskCache(nsIFile *aOldCacheDir,
|
||||
nsIFile *aNewCacheDir,
|
||||
const char *aCacheSubdir);
|
||||
|
||||
nsresult Init();
|
||||
void Shutdown();
|
||||
|
||||
static bool IsInitialized()
|
||||
{
|
||||
if (!gService) {
|
||||
return false;
|
||||
}
|
||||
return gService->mInitialized;
|
||||
}
|
||||
|
||||
static void AssertOwnsLock()
|
||||
{ gService->mLock.AssertCurrentThreadOwns(); }
|
||||
|
||||
static void LeavePrivateBrowsing();
|
||||
bool IsDoomListEmpty();
|
||||
|
||||
typedef bool (*DoomCheckFn)(nsCacheEntry* entry);
|
||||
|
||||
// Accessors to the disabled functionality
|
||||
nsresult CreateSessionInternal(const char * clientID,
|
||||
nsCacheStoragePolicy storagePolicy,
|
||||
bool streamBased,
|
||||
nsICacheSession **result);
|
||||
nsresult VisitEntriesInternal(nsICacheVisitor *visitor);
|
||||
nsresult EvictEntriesInternal(nsCacheStoragePolicy storagePolicy);
|
||||
|
||||
private:
|
||||
friend class nsCacheServiceAutoLock;
|
||||
friend class nsOfflineCacheDevice;
|
||||
friend class nsProcessRequestEvent;
|
||||
friend class nsSetSmartSizeEvent;
|
||||
friend class nsBlockOnCacheThreadEvent;
|
||||
friend class nsSetDiskSmartSizeCallback;
|
||||
friend class nsDoomEvent;
|
||||
friend class nsDisableOldMaxSmartSizePrefEvent;
|
||||
friend class nsDiskCacheMap;
|
||||
friend class nsAsyncDoomEvent;
|
||||
friend class nsCacheEntryDescriptor;
|
||||
|
||||
/**
|
||||
* Internal Methods
|
||||
*/
|
||||
|
||||
static void Lock();
|
||||
static void Lock(::mozilla::Telemetry::ID mainThreadLockerID);
|
||||
static void Unlock();
|
||||
void LockAcquired();
|
||||
void LockReleased();
|
||||
|
||||
nsresult CreateDiskDevice();
|
||||
nsresult CreateOfflineDevice();
|
||||
nsresult CreateCustomOfflineDevice(nsIFile *aProfileDir,
|
||||
int32_t aQuota,
|
||||
nsOfflineCacheDevice **aDevice);
|
||||
nsresult CreateMemoryDevice();
|
||||
|
||||
nsresult RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice);
|
||||
|
||||
nsresult CreateRequest(nsCacheSession * session,
|
||||
const nsACString & clientKey,
|
||||
nsCacheAccessMode accessRequested,
|
||||
bool blockingMode,
|
||||
nsICacheListener * listener,
|
||||
nsCacheRequest ** request);
|
||||
|
||||
nsresult DoomEntry_Internal(nsCacheEntry * entry,
|
||||
bool doProcessPendingRequests);
|
||||
|
||||
nsresult EvictEntriesForClient(const char * clientID,
|
||||
nsCacheStoragePolicy storagePolicy);
|
||||
|
||||
// Notifies request listener asynchronously on the request's thread, and
|
||||
// releases the descriptor on the request's thread. If this method fails,
|
||||
// the descriptor is not released.
|
||||
nsresult NotifyListener(nsCacheRequest * request,
|
||||
nsICacheEntryDescriptor * descriptor,
|
||||
nsCacheAccessMode accessGranted,
|
||||
nsresult error);
|
||||
|
||||
nsresult ActivateEntry(nsCacheRequest * request,
|
||||
nsCacheEntry ** entry,
|
||||
nsCacheEntry ** doomedEntry);
|
||||
|
||||
nsCacheDevice * EnsureEntryHasDevice(nsCacheEntry * entry);
|
||||
|
||||
nsCacheEntry * SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision);
|
||||
|
||||
void DeactivateEntry(nsCacheEntry * entry);
|
||||
|
||||
nsresult ProcessRequest(nsCacheRequest * request,
|
||||
bool calledFromOpenCacheEntry,
|
||||
nsICacheEntryDescriptor ** result);
|
||||
|
||||
nsresult ProcessPendingRequests(nsCacheEntry * entry);
|
||||
|
||||
void ClearDoomList(void);
|
||||
void DoomActiveEntries(DoomCheckFn check);
|
||||
void CloseAllStreams();
|
||||
void FireClearNetworkCacheStoredAnywhereNotification();
|
||||
|
||||
void LogCacheStatistics();
|
||||
|
||||
nsresult SetDiskSmartSize_Locked();
|
||||
|
||||
/**
|
||||
* Data Members
|
||||
*/
|
||||
|
||||
static nsCacheService * gService; // there can be only one...
|
||||
|
||||
nsCOMPtr<mozIStorageService> mStorageService;
|
||||
|
||||
nsCacheProfilePrefObserver * mObserver;
|
||||
|
||||
mozilla::Mutex mLock;
|
||||
mozilla::CondVar mCondVar;
|
||||
bool mNotified;
|
||||
|
||||
mozilla::Mutex mTimeStampLock;
|
||||
mozilla::TimeStamp mLockAcquiredTimeStamp;
|
||||
|
||||
nsCOMPtr<nsIThread> mCacheIOThread;
|
||||
|
||||
nsTArray<nsISupports*> mDoomedObjects;
|
||||
nsCOMPtr<nsITimer> mSmartSizeTimer;
|
||||
|
||||
bool mInitialized;
|
||||
bool mClearingEntries;
|
||||
|
||||
bool mEnableMemoryDevice;
|
||||
bool mEnableDiskDevice;
|
||||
bool mEnableOfflineDevice;
|
||||
|
||||
nsMemoryCacheDevice * mMemoryDevice;
|
||||
nsDiskCacheDevice * mDiskDevice;
|
||||
nsOfflineCacheDevice * mOfflineDevice;
|
||||
|
||||
nsRefPtrHashtable<nsStringHashKey, nsOfflineCacheDevice> mCustomOfflineDevices;
|
||||
|
||||
nsCacheEntryHashTable mActiveEntries;
|
||||
PRCList mDoomedEntries;
|
||||
|
||||
// stats
|
||||
|
||||
uint32_t mTotalEntries;
|
||||
uint32_t mCacheHits;
|
||||
uint32_t mCacheMisses;
|
||||
uint32_t mMaxKeyLength;
|
||||
uint32_t mMaxDataSize;
|
||||
uint32_t mMaxMetaSize;
|
||||
|
||||
// Unexpected error totals
|
||||
uint32_t mDeactivateFailures;
|
||||
uint32_t mDeactivatedUnboundEntries;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* nsCacheServiceAutoLock
|
||||
******************************************************************************/
|
||||
|
||||
#define LOCK_TELEM(x) \
|
||||
(::mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_##x)
|
||||
|
||||
// Instantiate this class to acquire the cache service lock for a particular
|
||||
// execution scope.
|
||||
class nsCacheServiceAutoLock {
|
||||
public:
|
||||
nsCacheServiceAutoLock() {
|
||||
nsCacheService::Lock();
|
||||
}
|
||||
explicit nsCacheServiceAutoLock(mozilla::Telemetry::ID mainThreadLockerID) {
|
||||
nsCacheService::Lock(mainThreadLockerID);
|
||||
}
|
||||
~nsCacheServiceAutoLock() {
|
||||
nsCacheService::Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _nsCacheService_h_
|
||||
147
netwerk/cache/nsCacheSession.cpp
vendored
Normal file
147
netwerk/cache/nsCacheSession.cpp
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCacheSession.h"
|
||||
#include "nsCacheService.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCacheSession, nsICacheSession)
|
||||
|
||||
nsCacheSession::nsCacheSession(const char * clientID,
|
||||
nsCacheStoragePolicy storagePolicy,
|
||||
bool streamBased)
|
||||
: mClientID(clientID),
|
||||
mInfo(0)
|
||||
{
|
||||
SetStoragePolicy(storagePolicy);
|
||||
|
||||
if (streamBased) MarkStreamBased();
|
||||
else SetStoragePolicy(nsICache::STORE_IN_MEMORY);
|
||||
|
||||
MarkPublic();
|
||||
|
||||
MarkDoomEntriesIfExpired();
|
||||
}
|
||||
|
||||
nsCacheSession::~nsCacheSession()
|
||||
{
|
||||
/* destructor code */
|
||||
// notify service we are going away?
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::GetDoomEntriesIfExpired(bool *result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = WillDoomEntriesIfExpired();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::SetProfileDirectory(nsIFile *profileDir)
|
||||
{
|
||||
if (StoragePolicy() != nsICache::STORE_OFFLINE && profileDir) {
|
||||
// Profile directory override is currently implemented only for
|
||||
// offline cache. This is an early failure to prevent the request
|
||||
// being processed before it would fail later because of inability
|
||||
// to assign a cache base dir.
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mProfileDir = profileDir;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::GetProfileDirectory(nsIFile **profileDir)
|
||||
{
|
||||
if (mProfileDir)
|
||||
NS_ADDREF(*profileDir = mProfileDir);
|
||||
else
|
||||
*profileDir = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::SetDoomEntriesIfExpired(bool doomEntriesIfExpired)
|
||||
{
|
||||
if (doomEntriesIfExpired) MarkDoomEntriesIfExpired();
|
||||
else ClearDoomEntriesIfExpired();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCacheSession::OpenCacheEntry(const nsACString & key,
|
||||
nsCacheAccessMode accessRequested,
|
||||
bool blockingMode,
|
||||
nsICacheEntryDescriptor ** result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (NS_IsMainThread())
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
else
|
||||
rv = nsCacheService::OpenCacheEntry(this,
|
||||
key,
|
||||
accessRequested,
|
||||
blockingMode,
|
||||
nullptr, // no listener
|
||||
result);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::AsyncOpenCacheEntry(const nsACString & key,
|
||||
nsCacheAccessMode accessRequested,
|
||||
nsICacheListener *listener,
|
||||
bool noWait)
|
||||
{
|
||||
nsresult rv;
|
||||
rv = nsCacheService::OpenCacheEntry(this,
|
||||
key,
|
||||
accessRequested,
|
||||
!noWait,
|
||||
listener,
|
||||
nullptr); // no result
|
||||
|
||||
if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) rv = NS_OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::EvictEntries()
|
||||
{
|
||||
return nsCacheService::EvictEntriesForSession(this);
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::IsStorageEnabled(bool *result)
|
||||
{
|
||||
|
||||
return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::DoomEntry(const nsACString &key,
|
||||
nsICacheListener *listener)
|
||||
{
|
||||
return nsCacheService::DoomEntry(this, key, listener);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::GetIsPrivate(bool* aPrivate)
|
||||
{
|
||||
*aPrivate = IsPrivate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCacheSession::SetIsPrivate(bool aPrivate)
|
||||
{
|
||||
if (aPrivate)
|
||||
MarkPrivate();
|
||||
else
|
||||
MarkPublic();
|
||||
return NS_OK;
|
||||
}
|
||||
67
netwerk/cache/nsCacheSession.h
vendored
Normal file
67
netwerk/cache/nsCacheSession.h
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCacheSession_h_
|
||||
#define _nsCacheSession_h_
|
||||
|
||||
#include "nspr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsICacheSession.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsCacheSession : public nsICacheSession
|
||||
{
|
||||
virtual ~nsCacheSession();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICACHESESSION
|
||||
|
||||
nsCacheSession(const char * clientID, nsCacheStoragePolicy storagePolicy, bool streamBased);
|
||||
|
||||
nsCString * ClientID() { return &mClientID; }
|
||||
|
||||
enum SessionInfo {
|
||||
eStoragePolicyMask = 0x000000FF,
|
||||
eStreamBasedMask = 0x00000100,
|
||||
eDoomEntriesIfExpiredMask = 0x00001000,
|
||||
ePrivateMask = 0x00010000
|
||||
};
|
||||
|
||||
void MarkStreamBased() { mInfo |= eStreamBasedMask; }
|
||||
void ClearStreamBased() { mInfo &= ~eStreamBasedMask; }
|
||||
bool IsStreamBased() { return (mInfo & eStreamBasedMask) != 0; }
|
||||
|
||||
void MarkDoomEntriesIfExpired() { mInfo |= eDoomEntriesIfExpiredMask; }
|
||||
void ClearDoomEntriesIfExpired() { mInfo &= ~eDoomEntriesIfExpiredMask; }
|
||||
bool WillDoomEntriesIfExpired() { return (0 != (mInfo & eDoomEntriesIfExpiredMask)); }
|
||||
|
||||
void MarkPrivate() { mInfo |= ePrivateMask; }
|
||||
void MarkPublic() { mInfo &= ~ePrivateMask; }
|
||||
bool IsPrivate() { return (mInfo & ePrivateMask) != 0; }
|
||||
nsCacheStoragePolicy StoragePolicy()
|
||||
{
|
||||
return (nsCacheStoragePolicy)(mInfo & eStoragePolicyMask);
|
||||
}
|
||||
|
||||
void SetStoragePolicy(nsCacheStoragePolicy policy)
|
||||
{
|
||||
NS_ASSERTION(policy <= 0xFF, "too many bits in nsCacheStoragePolicy");
|
||||
mInfo &= ~eStoragePolicyMask; // clear storage policy bits
|
||||
mInfo |= policy;
|
||||
}
|
||||
|
||||
nsIFile* ProfileDir() { return mProfileDir; }
|
||||
|
||||
private:
|
||||
nsCString mClientID;
|
||||
uint32_t mInfo;
|
||||
nsCOMPtr<nsIFile> mProfileDir;
|
||||
};
|
||||
|
||||
#endif // _nsCacheSession_h_
|
||||
89
netwerk/cache/nsCacheUtils.cpp
vendored
Normal file
89
netwerk/cache/nsCacheUtils.cpp
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCache.h"
|
||||
#include "nsCacheUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class nsDestroyThreadEvent : public Runnable {
|
||||
public:
|
||||
explicit nsDestroyThreadEvent(nsIThread *thread)
|
||||
: mThread(thread)
|
||||
{}
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mThread->Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
nsShutdownThread::nsShutdownThread(nsIThread *aThread)
|
||||
: mMonitor("nsShutdownThread.mMonitor")
|
||||
, mShuttingDown(false)
|
||||
, mThread(aThread)
|
||||
{
|
||||
}
|
||||
|
||||
nsShutdownThread::~nsShutdownThread()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsShutdownThread::Shutdown(nsIThread *aThread)
|
||||
{
|
||||
nsresult rv;
|
||||
RefPtr<nsDestroyThreadEvent> ev = new nsDestroyThreadEvent(aThread);
|
||||
rv = NS_DispatchToMainThread(ev);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Dispatching event in nsShutdownThread::Shutdown failed!");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsShutdownThread::BlockingShutdown(nsIThread *aThread)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
RefPtr<nsShutdownThread> st = new nsShutdownThread(aThread);
|
||||
nsCOMPtr<nsIThread> workerThread;
|
||||
|
||||
rv = NS_NewNamedThread("thread shutdown", getter_AddRefs(workerThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't create nsShutDownThread worker thread!");
|
||||
return rv;
|
||||
}
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(st->mMonitor);
|
||||
rv = workerThread->Dispatch(st, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING(
|
||||
"Dispatching event in nsShutdownThread::BlockingShutdown failed!");
|
||||
} else {
|
||||
st->mShuttingDown = true;
|
||||
while (st->mShuttingDown) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Shutdown(workerThread);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsShutdownThread::Run()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mThread->Shutdown();
|
||||
mShuttingDown = false;
|
||||
mon.Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
43
netwerk/cache/nsCacheUtils.h
vendored
Normal file
43
netwerk/cache/nsCacheUtils.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsCacheUtils_h_
|
||||
#define _nsCacheUtils_h_
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
/**
|
||||
* A class with utility methods for shutting down nsIThreads easily.
|
||||
*/
|
||||
class nsShutdownThread : public mozilla::Runnable {
|
||||
public:
|
||||
explicit nsShutdownThread(nsIThread *aThread);
|
||||
~nsShutdownThread();
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
/**
|
||||
* Shutdown ensures that aThread->Shutdown() is called on a main thread
|
||||
*/
|
||||
static nsresult Shutdown(nsIThread *aThread);
|
||||
|
||||
/**
|
||||
* BlockingShutdown ensures that by the time it returns, aThread->Shutdown() has
|
||||
* been called and no pending events have been processed on the current thread.
|
||||
*/
|
||||
static nsresult BlockingShutdown(nsIThread *aThread);
|
||||
|
||||
private:
|
||||
mozilla::Monitor mMonitor;
|
||||
bool mShuttingDown;
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
#endif
|
||||
454
netwerk/cache/nsDeleteDir.cpp
vendored
Normal file
454
netwerk/cache/nsDeleteDir.cpp
vendored
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsDeleteDir.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsCacheUtils.h"
|
||||
#include "prtime.h"
|
||||
#include <time.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class nsBlockOnBackgroundThreadEvent : public Runnable {
|
||||
public:
|
||||
nsBlockOnBackgroundThreadEvent() {}
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MutexAutoLock lock(nsDeleteDir::gInstance->mLock);
|
||||
nsDeleteDir::gInstance->mNotified = true;
|
||||
nsDeleteDir::gInstance->mCondVar.Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
nsDeleteDir * nsDeleteDir::gInstance = nullptr;
|
||||
|
||||
nsDeleteDir::nsDeleteDir()
|
||||
: mLock("nsDeleteDir.mLock"),
|
||||
mCondVar(mLock, "nsDeleteDir.mCondVar"),
|
||||
mNotified(false),
|
||||
mShutdownPending(false),
|
||||
mStopDeleting(false)
|
||||
{
|
||||
NS_ASSERTION(gInstance==nullptr, "multiple nsCacheService instances!");
|
||||
}
|
||||
|
||||
nsDeleteDir::~nsDeleteDir()
|
||||
{
|
||||
gInstance = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::Init()
|
||||
{
|
||||
if (gInstance)
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
|
||||
gInstance = new nsDeleteDir();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::Shutdown(bool finishDeleting)
|
||||
{
|
||||
if (!gInstance)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsCOMArray<nsIFile> dirsToRemove;
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
{
|
||||
MutexAutoLock lock(gInstance->mLock);
|
||||
NS_ASSERTION(!gInstance->mShutdownPending,
|
||||
"Unexpected state in nsDeleteDir::Shutdown()");
|
||||
gInstance->mShutdownPending = true;
|
||||
|
||||
if (!finishDeleting)
|
||||
gInstance->mStopDeleting = true;
|
||||
|
||||
// remove all pending timers
|
||||
for (int32_t i = gInstance->mTimers.Count(); i > 0; i--) {
|
||||
nsCOMPtr<nsITimer> timer = gInstance->mTimers[i-1];
|
||||
gInstance->mTimers.RemoveObjectAt(i-1);
|
||||
|
||||
nsCOMArray<nsIFile> *arg;
|
||||
timer->GetClosure((reinterpret_cast<void**>(&arg)));
|
||||
timer->Cancel();
|
||||
|
||||
if (finishDeleting)
|
||||
dirsToRemove.AppendObjects(*arg);
|
||||
|
||||
// delete argument passed to the timer
|
||||
delete arg;
|
||||
}
|
||||
|
||||
thread.swap(gInstance->mThread);
|
||||
if (thread) {
|
||||
// dispatch event and wait for it to run and notify us, so we know thread
|
||||
// has completed all work and can be shutdown
|
||||
nsCOMPtr<nsIRunnable> event = new nsBlockOnBackgroundThreadEvent();
|
||||
nsresult rv = thread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed dispatching block-event");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
gInstance->mNotified = false;
|
||||
while (!gInstance->mNotified) {
|
||||
gInstance->mCondVar.Wait();
|
||||
}
|
||||
nsShutdownThread::BlockingShutdown(thread);
|
||||
}
|
||||
}
|
||||
|
||||
delete gInstance;
|
||||
|
||||
for (int32_t i = 0; i < dirsToRemove.Count(); i++)
|
||||
dirsToRemove[i]->Remove(true);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::InitThread()
|
||||
{
|
||||
if (mThread)
|
||||
return NS_OK;
|
||||
|
||||
nsresult rv = NS_NewNamedThread("Cache Deleter", getter_AddRefs(mThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't create background thread");
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mThread);
|
||||
if (p) {
|
||||
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDeleteDir::DestroyThread()
|
||||
{
|
||||
if (!mThread)
|
||||
return;
|
||||
|
||||
if (mTimers.Count())
|
||||
// more work to do, so don't delete thread.
|
||||
return;
|
||||
|
||||
nsShutdownThread::Shutdown(mThread);
|
||||
mThread = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsDeleteDir::TimerCallback(nsITimer *aTimer, void *arg)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR> timer;
|
||||
{
|
||||
MutexAutoLock lock(gInstance->mLock);
|
||||
|
||||
int32_t idx = gInstance->mTimers.IndexOf(aTimer);
|
||||
if (idx == -1) {
|
||||
// Timer was canceled and removed during shutdown.
|
||||
return;
|
||||
}
|
||||
|
||||
gInstance->mTimers.RemoveObjectAt(idx);
|
||||
}
|
||||
|
||||
nsAutoPtr<nsCOMArray<nsIFile> > dirList;
|
||||
dirList = static_cast<nsCOMArray<nsIFile> *>(arg);
|
||||
|
||||
bool shuttingDown = false;
|
||||
|
||||
// Intentional extra braces to control variable sope.
|
||||
{
|
||||
// Low IO priority can only be set when running in the context of the
|
||||
// current thread. So this shouldn't be moved to where we set the priority
|
||||
// of the Cache deleter thread using the nsThread's NSPR priority constants.
|
||||
nsAutoLowPriorityIO autoLowPriority;
|
||||
for (int32_t i = 0; i < dirList->Count() && !shuttingDown; i++) {
|
||||
gInstance->RemoveDir((*dirList)[i], &shuttingDown);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(gInstance->mLock);
|
||||
gInstance->DestroyThread();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::DeleteDir(nsIFile *dirIn, bool moveToTrash, uint32_t delay)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_TRASHRENAME> timer;
|
||||
|
||||
if (!gInstance)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFile> trash, dir;
|
||||
|
||||
// Need to make a clone of this since we don't want to modify the input
|
||||
// file object.
|
||||
rv = dirIn->Clone(getter_AddRefs(dir));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (moveToTrash) {
|
||||
rv = GetTrashDir(dir, &trash);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
nsAutoCString origLeaf;
|
||||
rv = trash->GetNativeLeafName(origLeaf);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Append random number to the trash directory and check if it exists.
|
||||
srand(static_cast<unsigned>(PR_Now()));
|
||||
nsAutoCString leaf;
|
||||
for (int32_t i = 0; i < 10; i++) {
|
||||
leaf = origLeaf;
|
||||
leaf.AppendInt(rand());
|
||||
rv = trash->SetNativeLeafName(leaf);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
bool exists;
|
||||
if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
|
||||
break;
|
||||
}
|
||||
|
||||
leaf.Truncate();
|
||||
}
|
||||
|
||||
// Fail if we didn't find unused trash directory within the limit
|
||||
if (!leaf.Length())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
nsCOMPtr<nsIFile> parent;
|
||||
rv = trash->GetParent(getter_AddRefs(parent));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = dir->MoveToNative(parent, leaf);
|
||||
#else
|
||||
// Important: must rename directory w/o changing parent directory: else on
|
||||
// NTFS we'll wait (with cache lock) while nsIFile's ACL reset walks file
|
||||
// tree: was hanging GUI for *minutes* on large cache dirs.
|
||||
rv = dir->MoveToNative(nullptr, leaf);
|
||||
#endif
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
} else {
|
||||
// we want to pass a clone of the original off to the worker thread.
|
||||
trash.swap(dir);
|
||||
}
|
||||
|
||||
nsAutoPtr<nsCOMArray<nsIFile> > arg(new nsCOMArray<nsIFile>);
|
||||
arg->AppendObject(trash);
|
||||
|
||||
rv = gInstance->PostTimer(arg, delay);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
arg.forget();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::GetTrashDir(nsIFile *target, nsCOMPtr<nsIFile> *result)
|
||||
{
|
||||
nsresult rv;
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
// Try to use the app cache folder for cache trash on Android
|
||||
char* cachePath = getenv("CACHE_DIRECTORY");
|
||||
if (cachePath) {
|
||||
rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
|
||||
true, getter_AddRefs(*result));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Add a sub folder with the cache folder name
|
||||
nsAutoCString leaf;
|
||||
rv = target->GetNativeLeafName(leaf);
|
||||
(*result)->AppendNative(leaf);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
rv = target->Clone(getter_AddRefs(*result));
|
||||
}
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsAutoCString leaf;
|
||||
rv = (*result)->GetNativeLeafName(leaf);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
leaf.AppendLiteral(".Trash");
|
||||
|
||||
return (*result)->SetNativeLeafName(leaf);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::RemoveOldTrashes(nsIFile *cacheDir)
|
||||
{
|
||||
if (!gInstance)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIFile> trash;
|
||||
rv = GetTrashDir(cacheDir, &trash);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsAutoString trashName;
|
||||
rv = trash->GetLeafName(trashName);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIFile> parent;
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
rv = trash->GetParent(getter_AddRefs(parent));
|
||||
#else
|
||||
rv = cacheDir->GetParent(getter_AddRefs(parent));
|
||||
#endif
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
rv = parent->GetDirectoryEntries(getter_AddRefs(iter));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
bool more;
|
||||
nsCOMPtr<nsISupports> elem;
|
||||
nsAutoPtr<nsCOMArray<nsIFile> > dirList;
|
||||
|
||||
while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
|
||||
rv = iter->GetNext(getter_AddRefs(elem));
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
|
||||
if (!file)
|
||||
continue;
|
||||
|
||||
nsAutoString leafName;
|
||||
rv = file->GetLeafName(leafName);
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
// match all names that begin with the trash name (i.e. "Cache.Trash")
|
||||
if (Substring(leafName, 0, trashName.Length()).Equals(trashName)) {
|
||||
if (!dirList)
|
||||
dirList = new nsCOMArray<nsIFile>;
|
||||
dirList->AppendObject(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (dirList) {
|
||||
rv = gInstance->PostTimer(dirList, 90000);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
dirList.forget();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::PostTimer(void *arg, uint32_t delay)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
rv = InitThread();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = timer->SetTarget(mThread);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = timer->InitWithFuncCallback(TimerCallback, arg, delay,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
mTimers.AppendObject(timer);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeleteDir::RemoveDir(nsIFile *file, bool *stopDeleting)
|
||||
{
|
||||
nsresult rv;
|
||||
bool isLink;
|
||||
|
||||
rv = file->IsSymlink(&isLink);
|
||||
if (NS_FAILED(rv) || isLink)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
bool isDir;
|
||||
rv = file->IsDirectory(&isDir);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (isDir) {
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
rv = file->GetDirectoryEntries(getter_AddRefs(iter));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
bool more;
|
||||
nsCOMPtr<nsISupports> elem;
|
||||
while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
|
||||
rv = iter->GetNext(getter_AddRefs(elem));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Unexpected failure in nsDeleteDir::RemoveDir");
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file2 = do_QueryInterface(elem);
|
||||
if (!file2) {
|
||||
NS_WARNING("Unexpected failure in nsDeleteDir::RemoveDir");
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveDir(file2, stopDeleting);
|
||||
// No check for errors to remove as much as possible
|
||||
|
||||
if (*stopDeleting)
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
file->Remove(false);
|
||||
// No check for errors to remove as much as possible
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
if (mStopDeleting)
|
||||
*stopDeleting = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
80
netwerk/cache/nsDeleteDir.h
vendored
Normal file
80
netwerk/cache/nsDeleteDir.h
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDeleteDir_h__
|
||||
#define nsDeleteDir_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
|
||||
class nsIFile;
|
||||
class nsIThread;
|
||||
class nsITimer;
|
||||
|
||||
|
||||
class nsDeleteDir {
|
||||
public:
|
||||
nsDeleteDir();
|
||||
~nsDeleteDir();
|
||||
|
||||
static nsresult Init();
|
||||
static nsresult Shutdown(bool finishDeleting);
|
||||
|
||||
/**
|
||||
* This routine attempts to delete a directory that may contain some files
|
||||
* that are still in use. This latter point is only an issue on Windows and
|
||||
* a few other systems.
|
||||
*
|
||||
* If the moveToTrash parameter is true we first rename the given directory
|
||||
* "foo.Trash123" (where "foo" is the original directory name, and "123" is
|
||||
* a random number, in order to support multiple concurrent deletes). The
|
||||
* directory is then deleted, file-by-file, on a background thread.
|
||||
*
|
||||
* If the moveToTrash parameter is false, then the given directory is deleted
|
||||
* directly.
|
||||
*
|
||||
* If 'delay' is non-zero, the directory will not be deleted until the
|
||||
* specified number of milliseconds have passed. (The directory is still
|
||||
* renamed immediately if 'moveToTrash' is passed, so upon return it is safe
|
||||
* to create a directory with the same name).
|
||||
*/
|
||||
static nsresult DeleteDir(nsIFile *dir, bool moveToTrash, uint32_t delay = 0);
|
||||
|
||||
/**
|
||||
* Returns the trash directory corresponding to the given directory.
|
||||
*/
|
||||
static nsresult GetTrashDir(nsIFile *dir, nsCOMPtr<nsIFile> *result);
|
||||
|
||||
/**
|
||||
* Remove all trashes left from previous run. This function does nothing when
|
||||
* called second and more times.
|
||||
*/
|
||||
static nsresult RemoveOldTrashes(nsIFile *cacheDir);
|
||||
|
||||
static void TimerCallback(nsITimer *aTimer, void *arg);
|
||||
|
||||
private:
|
||||
friend class nsBlockOnBackgroundThreadEvent;
|
||||
friend class nsDestroyThreadEvent;
|
||||
|
||||
nsresult InitThread();
|
||||
void DestroyThread();
|
||||
nsresult PostTimer(void *arg, uint32_t delay);
|
||||
nsresult RemoveDir(nsIFile *file, bool *stopDeleting);
|
||||
|
||||
static nsDeleteDir * gInstance;
|
||||
mozilla::Mutex mLock;
|
||||
mozilla::CondVar mCondVar;
|
||||
bool mNotified;
|
||||
nsCOMArray<nsITimer> mTimers;
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
bool mShutdownPending;
|
||||
bool mStopDeleting;
|
||||
};
|
||||
|
||||
#endif // nsDeleteDir_h__
|
||||
72
netwerk/cache/nsDiskCache.h
vendored
Normal file
72
netwerk/cache/nsDiskCache.h
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#ifndef _nsDiskCache_h_
|
||||
#define _nsDiskCache_h_
|
||||
|
||||
#include "nsCacheEntry.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <winsock.h> // for htonl/ntohl
|
||||
#endif
|
||||
|
||||
|
||||
class nsDiskCache {
|
||||
public:
|
||||
enum {
|
||||
kCurrentVersion = 0x00010013 // format = 16 bits major version/16 bits minor version
|
||||
};
|
||||
|
||||
enum { kData, kMetaData };
|
||||
|
||||
// Stores the reason why the cache is corrupt.
|
||||
// Note: I'm only listing the enum values explicitly for easy mapping when
|
||||
// looking at telemetry data.
|
||||
enum CorruptCacheInfo {
|
||||
kNotCorrupt = 0,
|
||||
kInvalidArgPointer = 1,
|
||||
kUnexpectedError = 2,
|
||||
kOpenCacheMapError = 3,
|
||||
kBlockFilesShouldNotExist = 4,
|
||||
kOutOfMemory = 5,
|
||||
kCreateCacheSubdirectories = 6,
|
||||
kBlockFilesShouldExist = 7,
|
||||
kHeaderSizeNotRead = 8,
|
||||
kHeaderIsDirty = 9,
|
||||
kVersionMismatch = 10,
|
||||
kRecordsIncomplete = 11,
|
||||
kHeaderIncomplete = 12,
|
||||
kNotEnoughToRead = 13,
|
||||
kEntryCountIncorrect = 14,
|
||||
kCouldNotGetBlockFileForIndex = 15,
|
||||
kCouldNotCreateBlockFile = 16,
|
||||
kBlockFileSizeError = 17,
|
||||
kBlockFileBitMapWriteError = 18,
|
||||
kBlockFileSizeLessThanBitMap = 19,
|
||||
kBlockFileBitMapReadError = 20,
|
||||
kBlockFileEstimatedSizeError = 21,
|
||||
kFlushHeaderError = 22,
|
||||
kCacheCleanFilePathError = 23,
|
||||
kCacheCleanOpenFileError = 24,
|
||||
kCacheCleanTimerError = 25
|
||||
};
|
||||
|
||||
// Parameter initval initializes internal state of hash function. Hash values are different
|
||||
// for the same text when different initval is used. It can be any random number.
|
||||
//
|
||||
// It can be used for generating 64-bit hash value:
|
||||
// (uint64_t(Hash(key, initval1)) << 32) | Hash(key, initval2)
|
||||
//
|
||||
// It can be also used to hash multiple strings:
|
||||
// h = Hash(string1, 0);
|
||||
// h = Hash(string2, h);
|
||||
// ...
|
||||
static PLDHashNumber Hash(const char* key, PLDHashNumber initval=0);
|
||||
static nsresult Truncate(PRFileDesc * fd, uint32_t newEOF);
|
||||
};
|
||||
|
||||
#endif // _nsDiskCache_h_
|
||||
371
netwerk/cache/nsDiskCacheBinding.cpp
vendored
Normal file
371
netwerk/cache/nsDiskCacheBinding.cpp
vendored
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsCache.h"
|
||||
#include <limits.h>
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsDiskCacheBinding.h"
|
||||
#include "nsCacheService.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/******************************************************************************
|
||||
* static hash table callback functions
|
||||
*
|
||||
*****************************************************************************/
|
||||
struct HashTableEntry : PLDHashEntryHdr {
|
||||
nsDiskCacheBinding * mBinding;
|
||||
};
|
||||
|
||||
|
||||
static PLDHashNumber
|
||||
HashKey(const void *key)
|
||||
{
|
||||
return (PLDHashNumber) NS_PTR_TO_INT32(key);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
MatchEntry(const PLDHashEntryHdr * header,
|
||||
const void * key)
|
||||
{
|
||||
HashTableEntry * hashEntry = (HashTableEntry *) header;
|
||||
return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
|
||||
}
|
||||
|
||||
static void
|
||||
MoveEntry(PLDHashTable * /* table */,
|
||||
const PLDHashEntryHdr * src,
|
||||
PLDHashEntryHdr * dst)
|
||||
{
|
||||
((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ClearEntry(PLDHashTable * /* table */,
|
||||
PLDHashEntryHdr * header)
|
||||
{
|
||||
((HashTableEntry *)header)->mBinding = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility Functions
|
||||
*****************************************************************************/
|
||||
nsDiskCacheBinding *
|
||||
GetCacheEntryBinding(nsCacheEntry * entry)
|
||||
{
|
||||
return (nsDiskCacheBinding *) entry->Data();
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheBinding
|
||||
*****************************************************************************/
|
||||
|
||||
NS_IMPL_ISUPPORTS0(nsDiskCacheBinding)
|
||||
|
||||
nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
|
||||
: mCacheEntry(entry)
|
||||
, mStreamIO(nullptr)
|
||||
, mDeactivateEvent(nullptr)
|
||||
{
|
||||
NS_ASSERTION(record->ValidRecord(), "bad record");
|
||||
PR_INIT_CLIST(this);
|
||||
mRecord = *record;
|
||||
mDoomed = entry->IsDoomed();
|
||||
mGeneration = record->Generation(); // 0 == uninitialized, or data & meta using block files
|
||||
}
|
||||
|
||||
nsDiskCacheBinding::~nsDiskCacheBinding()
|
||||
{
|
||||
// Grab the cache lock since the binding is stored in nsCacheEntry::mData
|
||||
// and it is released using nsCacheService::ReleaseObject_Locked() which
|
||||
// releases the object outside the cache lock.
|
||||
nsCacheServiceAutoLock lock;
|
||||
|
||||
NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
|
||||
if (!PR_CLIST_IS_EMPTY(this))
|
||||
PR_REMOVE_LINK(this); // XXX why are we still on a list?
|
||||
|
||||
// sever streamIO/binding link
|
||||
if (mStreamIO) {
|
||||
if (NS_FAILED(mStreamIO->ClearBinding()))
|
||||
nsCacheService::DoomEntry(mCacheEntry);
|
||||
NS_RELEASE(mStreamIO);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDiskCacheBinding::EnsureStreamIO()
|
||||
{
|
||||
if (!mStreamIO) {
|
||||
mStreamIO = new nsDiskCacheStreamIO(this);
|
||||
if (!mStreamIO) return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(mStreamIO);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheBindery
|
||||
*
|
||||
* Keeps track of bound disk cache entries to detect for collisions.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
const PLDHashTableOps nsDiskCacheBindery::ops =
|
||||
{
|
||||
HashKey,
|
||||
MatchEntry,
|
||||
MoveEntry,
|
||||
ClearEntry
|
||||
};
|
||||
|
||||
|
||||
nsDiskCacheBindery::nsDiskCacheBindery()
|
||||
: table(&ops, sizeof(HashTableEntry), kInitialTableLength)
|
||||
, initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
nsDiskCacheBindery::~nsDiskCacheBindery()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsDiskCacheBindery::Init()
|
||||
{
|
||||
table.ClearAndPrepareForLength(kInitialTableLength);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsDiskCacheBindery::Reset()
|
||||
{
|
||||
if (initialized) {
|
||||
table.ClearAndPrepareForLength(kInitialTableLength);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsDiskCacheBinding *
|
||||
nsDiskCacheBindery::CreateBinding(nsCacheEntry * entry,
|
||||
nsDiskCacheRecord * record)
|
||||
{
|
||||
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
||||
nsCOMPtr<nsISupports> data = entry->Data();
|
||||
if (data) {
|
||||
NS_ERROR("cache entry already has bind data");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record);
|
||||
if (!binding) return nullptr;
|
||||
|
||||
// give ownership of the binding to the entry
|
||||
entry->SetData(binding);
|
||||
|
||||
// add binding to collision detection system
|
||||
nsresult rv = AddBinding(binding);
|
||||
if (NS_FAILED(rv)) {
|
||||
entry->SetData(nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FindActiveEntry : to find active colliding entry so we can doom it
|
||||
*/
|
||||
nsDiskCacheBinding *
|
||||
nsDiskCacheBindery::FindActiveBinding(uint32_t hashNumber)
|
||||
{
|
||||
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
||||
// find hash entry for key
|
||||
auto hashEntry = static_cast<HashTableEntry*>
|
||||
(table.Search((void*)(uintptr_t)hashNumber));
|
||||
if (!hashEntry) return nullptr;
|
||||
|
||||
// walk list looking for active entry
|
||||
NS_ASSERTION(hashEntry->mBinding, "hash entry left with no binding");
|
||||
nsDiskCacheBinding * binding = hashEntry->mBinding;
|
||||
while (binding->mCacheEntry->IsDoomed()) {
|
||||
binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
||||
if (binding == hashEntry->mBinding) return nullptr;
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* AddBinding
|
||||
*
|
||||
* Called from FindEntry() if we read an entry off of disk
|
||||
* - it may already have a generation number
|
||||
* - a generation number conflict is an error
|
||||
*
|
||||
* Called from BindEntry()
|
||||
* - a generation number needs to be assigned
|
||||
*/
|
||||
nsresult
|
||||
nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(binding);
|
||||
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
||||
|
||||
// find hash entry for key
|
||||
auto hashEntry = static_cast<HashTableEntry*>
|
||||
(table.Add((void*)(uintptr_t)binding->mRecord.HashNumber(), fallible));
|
||||
if (!hashEntry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (hashEntry->mBinding == nullptr) {
|
||||
hashEntry->mBinding = binding;
|
||||
if (binding->mGeneration == 0)
|
||||
binding->mGeneration = 1; // if generation uninitialized, set it to 1
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// insert binding in generation order
|
||||
nsDiskCacheBinding * p = hashEntry->mBinding;
|
||||
bool calcGeneration = (binding->mGeneration == 0); // do we need to calculate generation?
|
||||
if (calcGeneration) binding->mGeneration = 1; // initialize to 1 if uninitialized
|
||||
while (1) {
|
||||
|
||||
if (binding->mGeneration < p->mGeneration) {
|
||||
// here we are
|
||||
PR_INSERT_BEFORE(binding, p);
|
||||
if (hashEntry->mBinding == p)
|
||||
hashEntry->mBinding = binding;
|
||||
break;
|
||||
}
|
||||
|
||||
if (binding->mGeneration == p->mGeneration) {
|
||||
if (calcGeneration) ++binding->mGeneration; // try the next generation
|
||||
else {
|
||||
NS_ERROR("### disk cache: generations collide!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
p = (nsDiskCacheBinding *)PR_NEXT_LINK(p);
|
||||
if (p == hashEntry->mBinding) {
|
||||
// end of line: insert here or die
|
||||
p = (nsDiskCacheBinding *)PR_PREV_LINK(p); // back up and check generation
|
||||
if (p->mGeneration == 255) {
|
||||
NS_WARNING("### disk cache: generation capacity at full");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
PR_INSERT_BEFORE(binding, hashEntry->mBinding);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* RemoveBinding : remove binding from collision detection on deactivation
|
||||
*/
|
||||
void
|
||||
nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
|
||||
{
|
||||
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
||||
if (!initialized) return;
|
||||
|
||||
void* key = (void *)(uintptr_t)binding->mRecord.HashNumber();
|
||||
auto hashEntry =
|
||||
static_cast<HashTableEntry*>(table.Search((void*)(uintptr_t) key));
|
||||
if (!hashEntry) {
|
||||
NS_WARNING("### disk cache: binding not in hashtable!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (binding == hashEntry->mBinding) {
|
||||
if (PR_CLIST_IS_EMPTY(binding)) {
|
||||
// remove this hash entry
|
||||
table.Remove((void*)(uintptr_t) binding->mRecord.HashNumber());
|
||||
return;
|
||||
|
||||
} else {
|
||||
// promote next binding to head, and unlink this binding
|
||||
hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
||||
}
|
||||
}
|
||||
PR_REMOVE_AND_INIT_LINK(binding);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActiveBindings: return true if any bindings have open descriptors.
|
||||
*/
|
||||
bool
|
||||
nsDiskCacheBindery::ActiveBindings()
|
||||
{
|
||||
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
||||
if (!initialized) return false;
|
||||
|
||||
for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
|
||||
auto entry = static_cast<HashTableEntry*>(iter.Get());
|
||||
nsDiskCacheBinding* binding = entry->mBinding;
|
||||
nsDiskCacheBinding* head = binding;
|
||||
do {
|
||||
if (binding->IsActive()) {
|
||||
return true;
|
||||
}
|
||||
binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
||||
} while (binding != head);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* SizeOfExcludingThis: return the amount of heap memory (bytes) being used by
|
||||
* the bindery.
|
||||
*/
|
||||
size_t
|
||||
nsDiskCacheBindery::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
|
||||
if (!initialized) return 0;
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
|
||||
auto entry = static_cast<HashTableEntry*>(iter.Get());
|
||||
nsDiskCacheBinding* binding = entry->mBinding;
|
||||
|
||||
nsDiskCacheBinding* head = binding;
|
||||
do {
|
||||
size += aMallocSizeOf(binding);
|
||||
if (binding->mStreamIO) {
|
||||
size += binding->mStreamIO->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
// No good way to get at mDeactivateEvent internals for proper
|
||||
// size, so we use this as an estimate.
|
||||
if (binding->mDeactivateEvent) {
|
||||
size += aMallocSizeOf(binding->mDeactivateEvent);
|
||||
}
|
||||
binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
|
||||
} while (binding != head);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
123
netwerk/cache/nsDiskCacheBinding.h
vendored
Normal file
123
netwerk/cache/nsDiskCacheBinding.h
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#ifndef _nsDiskCacheBinding_h_
|
||||
#define _nsDiskCacheBinding_h_
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nspr.h"
|
||||
#include "PLDHashTable.h"
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsCacheEntry.h"
|
||||
|
||||
#include "nsDiskCacheMap.h"
|
||||
#include "nsDiskCacheStreams.h"
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheBinding
|
||||
*
|
||||
* Created for disk cache specific data and stored in nsCacheEntry.mData as
|
||||
* an nsISupports. Also stored in nsDiskCacheHashTable, with collisions
|
||||
* linked by the PRCList.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
class nsDiskCacheDeviceDeactivateEntryEvent;
|
||||
|
||||
class nsDiskCacheBinding : public nsISupports, public PRCList {
|
||||
virtual ~nsDiskCacheBinding();
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record);
|
||||
|
||||
nsresult EnsureStreamIO();
|
||||
bool IsActive() { return mCacheEntry != nullptr;}
|
||||
|
||||
// XXX make friends
|
||||
public:
|
||||
nsCacheEntry* mCacheEntry; // back pointer to parent nsCacheEntry
|
||||
nsDiskCacheRecord mRecord;
|
||||
nsDiskCacheStreamIO* mStreamIO; // strong reference
|
||||
bool mDoomed; // record is not stored in cache map
|
||||
uint8_t mGeneration; // possibly just reservation
|
||||
|
||||
// If set, points to a pending event which will deactivate |mCacheEntry|.
|
||||
// If not set then either |mCacheEntry| is not deactivated, or it has been
|
||||
// deactivated but the device returned it from FindEntry() before the event
|
||||
// fired. In both two latter cases this binding is to be considered valid.
|
||||
nsDiskCacheDeviceDeactivateEntryEvent *mDeactivateEvent;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility Functions
|
||||
*****************************************************************************/
|
||||
|
||||
nsDiskCacheBinding * GetCacheEntryBinding(nsCacheEntry * entry);
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheBindery
|
||||
*
|
||||
* Used to keep track of nsDiskCacheBinding associated with active/bound (and
|
||||
* possibly doomed) entries. Lookups on 4 byte disk hash to find collisions
|
||||
* (which need to be doomed, instead of just evicted. Collisions are linked
|
||||
* using a PRCList to keep track of current generation number.
|
||||
*
|
||||
* Used to detect hash number collisions, and find available generation numbers.
|
||||
*
|
||||
* Not all nsDiskCacheBinding have a generation number.
|
||||
*
|
||||
* Generation numbers may be aquired late, or lost (when data fits in block file)
|
||||
*
|
||||
* Collisions can occur:
|
||||
* BindEntry() - hashnumbers collide (possibly different keys)
|
||||
*
|
||||
* Generation number required:
|
||||
* DeactivateEntry() - metadata written to disk, may require file
|
||||
* GetFileForEntry() - force data to require file
|
||||
* writing to stream - data size may require file
|
||||
*
|
||||
* Binding can be kept in PRCList in order of generation numbers.
|
||||
* Binding with no generation number can be Appended to PRCList (last).
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
class nsDiskCacheBindery {
|
||||
public:
|
||||
nsDiskCacheBindery();
|
||||
~nsDiskCacheBindery();
|
||||
|
||||
void Init();
|
||||
void Reset();
|
||||
|
||||
nsDiskCacheBinding * CreateBinding(nsCacheEntry * entry,
|
||||
nsDiskCacheRecord * record);
|
||||
|
||||
nsDiskCacheBinding * FindActiveBinding(uint32_t hashNumber);
|
||||
void RemoveBinding(nsDiskCacheBinding * binding);
|
||||
bool ActiveBindings();
|
||||
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
private:
|
||||
nsresult AddBinding(nsDiskCacheBinding * binding);
|
||||
|
||||
// member variables
|
||||
static const PLDHashTableOps ops;
|
||||
PLDHashTable table;
|
||||
bool initialized;
|
||||
|
||||
static const uint32_t kInitialTableLength = 0;
|
||||
};
|
||||
|
||||
#endif /* _nsDiskCacheBinding_h_ */
|
||||
404
netwerk/cache/nsDiskCacheBlockFile.cpp
vendored
Normal file
404
netwerk/cache/nsDiskCacheBlockFile.cpp
vendored
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCache.h"
|
||||
#include "nsDiskCache.h"
|
||||
#include "nsDiskCacheBlockFile.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheBlockFile -
|
||||
*****************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
* Open
|
||||
*****************************************************************************/
|
||||
nsresult
|
||||
nsDiskCacheBlockFile::Open(nsIFile * blockFile,
|
||||
uint32_t blockSize,
|
||||
uint32_t bitMapSize,
|
||||
nsDiskCache::CorruptCacheInfo * corruptInfo)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(corruptInfo);
|
||||
*corruptInfo = nsDiskCache::kUnexpectedError;
|
||||
|
||||
if (bitMapSize % 32) {
|
||||
*corruptInfo = nsDiskCache::kInvalidArgPointer;
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mBlockSize = blockSize;
|
||||
mBitMapWords = bitMapSize / 32;
|
||||
uint32_t bitMapBytes = mBitMapWords * 4;
|
||||
|
||||
// open the file - restricted to user, the data could be confidential
|
||||
nsresult rv = blockFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mFD);
|
||||
if (NS_FAILED(rv)) {
|
||||
*corruptInfo = nsDiskCache::kCouldNotCreateBlockFile;
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open "
|
||||
"[this=%p] unable to open or create file: %d",
|
||||
this, rv));
|
||||
return rv; // unable to open or create file
|
||||
}
|
||||
|
||||
// allocate bit map buffer
|
||||
mBitMap = new uint32_t[mBitMapWords];
|
||||
|
||||
// check if we just creating the file
|
||||
mFileSize = PR_Available(mFD);
|
||||
if (mFileSize < 0) {
|
||||
// XXX an error occurred. We could call PR_GetError(), but how would that help?
|
||||
*corruptInfo = nsDiskCache::kBlockFileSizeError;
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
goto error_exit;
|
||||
}
|
||||
if (mFileSize == 0) {
|
||||
// initialize bit map and write it
|
||||
memset(mBitMap, 0, bitMapBytes);
|
||||
if (!Write(0, mBitMap, bitMapBytes)) {
|
||||
*corruptInfo = nsDiskCache::kBlockFileBitMapWriteError;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
} else if ((uint32_t)mFileSize < bitMapBytes) {
|
||||
*corruptInfo = nsDiskCache::kBlockFileSizeLessThanBitMap;
|
||||
rv = NS_ERROR_UNEXPECTED; // XXX NS_ERROR_CACHE_INVALID;
|
||||
goto error_exit;
|
||||
|
||||
} else {
|
||||
// read the bit map
|
||||
const int32_t bytesRead = PR_Read(mFD, mBitMap, bitMapBytes);
|
||||
if ((bytesRead < 0) || ((uint32_t)bytesRead < bitMapBytes)) {
|
||||
*corruptInfo = nsDiskCache::kBlockFileBitMapReadError;
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
goto error_exit;
|
||||
}
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
// Swap from network format
|
||||
for (unsigned int i = 0; i < mBitMapWords; ++i)
|
||||
mBitMap[i] = ntohl(mBitMap[i]);
|
||||
#endif
|
||||
// validate block file size
|
||||
// Because not whole blocks are written, the size may be a
|
||||
// little bit smaller than used blocks times blocksize,
|
||||
// because the last block will generally not be 'whole'.
|
||||
const uint32_t estimatedSize = CalcBlockFileSize();
|
||||
if ((uint32_t)mFileSize + blockSize < estimatedSize) {
|
||||
*corruptInfo = nsDiskCache::kBlockFileEstimatedSizeError;
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open [this=%p] succeeded",
|
||||
this));
|
||||
return NS_OK;
|
||||
|
||||
error_exit:
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open [this=%p] failed with "
|
||||
"error %d", this, rv));
|
||||
Close(false);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Close
|
||||
*****************************************************************************/
|
||||
nsresult
|
||||
nsDiskCacheBlockFile::Close(bool flush)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mFD) {
|
||||
if (flush)
|
||||
rv = FlushBitMap();
|
||||
PRStatus err = PR_Close(mFD);
|
||||
if (NS_SUCCEEDED(rv) && (err != PR_SUCCESS))
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
mFD = nullptr;
|
||||
}
|
||||
|
||||
if (mBitMap) {
|
||||
delete [] mBitMap;
|
||||
mBitMap = nullptr;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* AllocateBlocks
|
||||
*
|
||||
* Allocates 1-4 blocks, using a first fit strategy,
|
||||
* so that no group of blocks spans a quad block boundary.
|
||||
*
|
||||
* Returns block number of first block allocated or -1 on failure.
|
||||
*
|
||||
*****************************************************************************/
|
||||
int32_t
|
||||
nsDiskCacheBlockFile::AllocateBlocks(int32_t numBlocks)
|
||||
{
|
||||
const int maxPos = 32 - numBlocks;
|
||||
const uint32_t mask = (0x01 << numBlocks) - 1;
|
||||
for (unsigned int i = 0; i < mBitMapWords; ++i) {
|
||||
uint32_t mapWord = ~mBitMap[i]; // flip bits so free bits are 1
|
||||
if (mapWord) { // At least one free bit
|
||||
// Binary search for first free bit in word
|
||||
int bit = 0;
|
||||
if ((mapWord & 0x0FFFF) == 0) { bit |= 16; mapWord >>= 16; }
|
||||
if ((mapWord & 0x000FF) == 0) { bit |= 8; mapWord >>= 8; }
|
||||
if ((mapWord & 0x0000F) == 0) { bit |= 4; mapWord >>= 4; }
|
||||
if ((mapWord & 0x00003) == 0) { bit |= 2; mapWord >>= 2; }
|
||||
if ((mapWord & 0x00001) == 0) { bit |= 1; mapWord >>= 1; }
|
||||
// Find first fit for mask
|
||||
for (; bit <= maxPos; ++bit) {
|
||||
// all bits selected by mask are 1, so free
|
||||
if ((mask & mapWord) == mask) {
|
||||
mBitMap[i] |= mask << bit;
|
||||
mBitMapDirty = true;
|
||||
return (int32_t)i * 32 + bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* DeallocateBlocks
|
||||
*****************************************************************************/
|
||||
nsresult
|
||||
nsDiskCacheBlockFile::DeallocateBlocks( int32_t startBlock, int32_t numBlocks)
|
||||
{
|
||||
if (!mFD) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
if ((startBlock < 0) || ((uint32_t)startBlock > mBitMapWords * 32 - 1) ||
|
||||
(numBlocks < 1) || (numBlocks > 4))
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
|
||||
const int32_t startWord = startBlock >> 5; // Divide by 32
|
||||
const uint32_t startBit = startBlock & 31; // Modulo by 32
|
||||
|
||||
// make sure requested deallocation doesn't span a word boundary
|
||||
if (startBit + numBlocks > 32) return NS_ERROR_UNEXPECTED;
|
||||
uint32_t mask = ((0x01 << numBlocks) - 1) << startBit;
|
||||
|
||||
// make sure requested deallocation is currently allocated
|
||||
if ((mBitMap[startWord] & mask) != mask) return NS_ERROR_ABORT;
|
||||
|
||||
mBitMap[startWord] ^= mask; // flips the bits off;
|
||||
mBitMapDirty = true;
|
||||
// XXX rv = FlushBitMap(); // coherency vs. performance
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* WriteBlocks
|
||||
*****************************************************************************/
|
||||
nsresult
|
||||
nsDiskCacheBlockFile::WriteBlocks( void * buffer,
|
||||
uint32_t size,
|
||||
int32_t numBlocks,
|
||||
int32_t * startBlock)
|
||||
{
|
||||
// presume buffer != nullptr and startBlock != nullptr
|
||||
NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
// allocate some blocks in the cache block file
|
||||
*startBlock = AllocateBlocks(numBlocks);
|
||||
if (*startBlock < 0)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
// seek to block position
|
||||
int32_t blockPos = mBitMapWords * 4 + *startBlock * mBlockSize;
|
||||
|
||||
// write the blocks
|
||||
return Write(blockPos, buffer, size) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* ReadBlocks
|
||||
*****************************************************************************/
|
||||
nsresult
|
||||
nsDiskCacheBlockFile::ReadBlocks( void * buffer,
|
||||
int32_t startBlock,
|
||||
int32_t numBlocks,
|
||||
int32_t * bytesRead)
|
||||
{
|
||||
// presume buffer != nullptr and bytesRead != bytesRead
|
||||
|
||||
if (!mFD) return NS_ERROR_NOT_AVAILABLE;
|
||||
nsresult rv = VerifyAllocation(startBlock, numBlocks);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// seek to block position
|
||||
int32_t blockPos = mBitMapWords * 4 + startBlock * mBlockSize;
|
||||
int32_t filePos = PR_Seek(mFD, blockPos, PR_SEEK_SET);
|
||||
if (filePos != blockPos) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// read the blocks
|
||||
int32_t bytesToRead = *bytesRead;
|
||||
if ((bytesToRead <= 0) || ((uint32_t)bytesToRead > mBlockSize * numBlocks)) {
|
||||
bytesToRead = mBlockSize * numBlocks;
|
||||
}
|
||||
*bytesRead = PR_Read(mFD, buffer, bytesToRead);
|
||||
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Read [this=%p] "
|
||||
"returned %d / %d bytes", this, *bytesRead, bytesToRead));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FlushBitMap
|
||||
*****************************************************************************/
|
||||
nsresult
|
||||
nsDiskCacheBlockFile::FlushBitMap()
|
||||
{
|
||||
if (!mBitMapDirty) return NS_OK;
|
||||
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
uint32_t *bitmap = new uint32_t[mBitMapWords];
|
||||
// Copy and swap to network format
|
||||
uint32_t *p = bitmap;
|
||||
for (unsigned int i = 0; i < mBitMapWords; ++i, ++p)
|
||||
*p = htonl(mBitMap[i]);
|
||||
#else
|
||||
uint32_t *bitmap = mBitMap;
|
||||
#endif
|
||||
|
||||
// write bitmap
|
||||
bool written = Write(0, bitmap, mBitMapWords * 4);
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
delete [] bitmap;
|
||||
#endif
|
||||
if (!written)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
PRStatus err = PR_Sync(mFD);
|
||||
if (err != PR_SUCCESS) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
mBitMapDirty = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* VerifyAllocation
|
||||
*
|
||||
* Return values:
|
||||
* NS_OK if all bits are marked allocated
|
||||
* NS_ERROR_ILLEGAL_VALUE if parameters don't obey constraints
|
||||
* NS_ERROR_FAILURE if some or all the bits are marked unallocated
|
||||
*
|
||||
*****************************************************************************/
|
||||
nsresult
|
||||
nsDiskCacheBlockFile::VerifyAllocation( int32_t startBlock, int32_t numBlocks)
|
||||
{
|
||||
if ((startBlock < 0) || ((uint32_t)startBlock > mBitMapWords * 32 - 1) ||
|
||||
(numBlocks < 1) || (numBlocks > 4))
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
|
||||
const int32_t startWord = startBlock >> 5; // Divide by 32
|
||||
const uint32_t startBit = startBlock & 31; // Modulo by 32
|
||||
|
||||
// make sure requested deallocation doesn't span a word boundary
|
||||
if (startBit + numBlocks > 32) return NS_ERROR_ILLEGAL_VALUE;
|
||||
uint32_t mask = ((0x01 << numBlocks) - 1) << startBit;
|
||||
|
||||
// check if all specified blocks are currently allocated
|
||||
if ((mBitMap[startWord] & mask) != mask) return NS_ERROR_FAILURE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* CalcBlockFileSize
|
||||
*
|
||||
* Return size of the block file according to the bits set in mBitmap
|
||||
*
|
||||
*****************************************************************************/
|
||||
uint32_t
|
||||
nsDiskCacheBlockFile::CalcBlockFileSize()
|
||||
{
|
||||
// search for last byte in mBitMap with allocated bits
|
||||
uint32_t estimatedSize = mBitMapWords * 4;
|
||||
int32_t i = mBitMapWords;
|
||||
while (--i >= 0) {
|
||||
if (mBitMap[i]) break;
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
// binary search to find last allocated bit in byte
|
||||
uint32_t mapWord = mBitMap[i];
|
||||
uint32_t lastBit = 31;
|
||||
if ((mapWord & 0xFFFF0000) == 0) { lastBit ^= 16; mapWord <<= 16; }
|
||||
if ((mapWord & 0xFF000000) == 0) { lastBit ^= 8; mapWord <<= 8; }
|
||||
if ((mapWord & 0xF0000000) == 0) { lastBit ^= 4; mapWord <<= 4; }
|
||||
if ((mapWord & 0xC0000000) == 0) { lastBit ^= 2; mapWord <<= 2; }
|
||||
if ((mapWord & 0x80000000) == 0) { lastBit ^= 1; mapWord <<= 1; }
|
||||
estimatedSize += (i * 32 + lastBit + 1) * mBlockSize;
|
||||
}
|
||||
|
||||
return estimatedSize;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Write
|
||||
*
|
||||
* Wrapper around PR_Write that grows file in larger chunks to combat fragmentation
|
||||
*
|
||||
*****************************************************************************/
|
||||
bool
|
||||
nsDiskCacheBlockFile::Write(int32_t offset, const void *buf, int32_t amount)
|
||||
{
|
||||
/* Grow the file to 4mb right away, then double it until the file grows to 20mb.
|
||||
20mb is a magic threshold because OSX stops autodefragging files bigger than that.
|
||||
Beyond 20mb grow in 4mb chunks.
|
||||
*/
|
||||
const int32_t upTo = offset + amount;
|
||||
// Use a conservative definition of 20MB
|
||||
const int32_t minPreallocate = 4*1024*1024;
|
||||
const int32_t maxPreallocate = 20*1000*1000;
|
||||
if (mFileSize < upTo) {
|
||||
// maximal file size
|
||||
const int32_t maxFileSize = mBitMapWords * 4 * (mBlockSize * 8 + 1);
|
||||
if (upTo > maxPreallocate) {
|
||||
// grow the file as a multiple of minPreallocate
|
||||
mFileSize = ((upTo + minPreallocate - 1) / minPreallocate) * minPreallocate;
|
||||
} else {
|
||||
// Grow quickly between 1MB to 20MB
|
||||
if (mFileSize)
|
||||
while(mFileSize < upTo)
|
||||
mFileSize *= 2;
|
||||
mFileSize = clamped(mFileSize, minPreallocate, maxPreallocate);
|
||||
}
|
||||
mFileSize = std::min(mFileSize, maxFileSize);
|
||||
#if !defined(XP_MACOSX)
|
||||
mozilla::fallocate(mFD, mFileSize);
|
||||
#endif
|
||||
}
|
||||
if (PR_Seek(mFD, offset, PR_SEEK_SET) != offset)
|
||||
return false;
|
||||
return PR_Write(mFD, buf, amount) == amount;
|
||||
}
|
||||
|
||||
size_t
|
||||
nsDiskCacheBlockFile::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
return aMallocSizeOf(mBitMap) + aMallocSizeOf(mFD);
|
||||
}
|
||||
69
netwerk/cache/nsDiskCacheBlockFile.h
vendored
Normal file
69
netwerk/cache/nsDiskCacheBlockFile.h
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsDiskCacheBlockFile_h_
|
||||
#define _nsDiskCacheBlockFile_h_
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsDiskCache.h"
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheBlockFile
|
||||
*
|
||||
* The structure of a cache block file is a 4096 bytes bit map, followed by
|
||||
* some number of blocks of mBlockSize. The creator of a
|
||||
* nsDiskCacheBlockFile object must provide the block size for a given file.
|
||||
*
|
||||
*****************************************************************************/
|
||||
class nsDiskCacheBlockFile {
|
||||
public:
|
||||
nsDiskCacheBlockFile()
|
||||
: mFD(nullptr)
|
||||
, mBitMap(nullptr)
|
||||
, mBlockSize(0)
|
||||
, mBitMapWords(0)
|
||||
, mFileSize(0)
|
||||
, mBitMapDirty(false)
|
||||
{}
|
||||
~nsDiskCacheBlockFile() { (void) Close(true); }
|
||||
|
||||
nsresult Open( nsIFile * blockFile, uint32_t blockSize,
|
||||
uint32_t bitMapSize, nsDiskCache::CorruptCacheInfo * corruptInfo);
|
||||
nsresult Close(bool flush);
|
||||
|
||||
/*
|
||||
* Trim
|
||||
* Truncates the block file to the end of the last allocated block.
|
||||
*/
|
||||
nsresult Trim() { return nsDiskCache::Truncate(mFD, CalcBlockFileSize()); }
|
||||
nsresult DeallocateBlocks( int32_t startBlock, int32_t numBlocks);
|
||||
nsresult WriteBlocks( void * buffer, uint32_t size, int32_t numBlocks,
|
||||
int32_t * startBlock);
|
||||
nsresult ReadBlocks( void * buffer, int32_t startBlock, int32_t numBlocks,
|
||||
int32_t * bytesRead);
|
||||
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
private:
|
||||
nsresult FlushBitMap();
|
||||
int32_t AllocateBlocks( int32_t numBlocks);
|
||||
nsresult VerifyAllocation( int32_t startBlock, int32_t numBLocks);
|
||||
uint32_t CalcBlockFileSize();
|
||||
bool Write(int32_t offset, const void *buf, int32_t amount);
|
||||
|
||||
/**
|
||||
* Data members
|
||||
*/
|
||||
PRFileDesc * mFD;
|
||||
uint32_t * mBitMap; // XXX future: array of bit map blocks
|
||||
uint32_t mBlockSize;
|
||||
uint32_t mBitMapWords;
|
||||
int32_t mFileSize;
|
||||
bool mBitMapDirty;
|
||||
};
|
||||
|
||||
#endif // _nsDiskCacheBlockFile_h_
|
||||
1149
netwerk/cache/nsDiskCacheDevice.cpp
vendored
Normal file
1149
netwerk/cache/nsDiskCacheDevice.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
116
netwerk/cache/nsDiskCacheDevice.h
vendored
Normal file
116
netwerk/cache/nsDiskCacheDevice.h
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsDiskCacheDevice_h_
|
||||
#define _nsDiskCacheDevice_h_
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsCacheDevice.h"
|
||||
#include "nsDiskCacheBinding.h"
|
||||
#include "nsDiskCacheBlockFile.h"
|
||||
#include "nsDiskCacheEntry.h"
|
||||
|
||||
#include "nsIFile.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsCOMArray.h"
|
||||
|
||||
class nsDiskCacheMap;
|
||||
|
||||
|
||||
class nsDiskCacheDevice final : public nsCacheDevice {
|
||||
public:
|
||||
nsDiskCacheDevice();
|
||||
virtual ~nsDiskCacheDevice();
|
||||
|
||||
virtual nsresult Init();
|
||||
virtual nsresult Shutdown();
|
||||
|
||||
virtual const char * GetDeviceID(void);
|
||||
virtual nsCacheEntry * FindEntry(nsCString * key, bool *collision);
|
||||
virtual nsresult DeactivateEntry(nsCacheEntry * entry);
|
||||
virtual nsresult BindEntry(nsCacheEntry * entry);
|
||||
virtual void DoomEntry( nsCacheEntry * entry );
|
||||
|
||||
virtual nsresult OpenInputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIInputStream ** result);
|
||||
|
||||
virtual nsresult OpenOutputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIOutputStream ** result);
|
||||
|
||||
virtual nsresult GetFileForEntry(nsCacheEntry * entry,
|
||||
nsIFile ** result);
|
||||
|
||||
virtual nsresult OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize);
|
||||
|
||||
virtual nsresult Visit(nsICacheVisitor * visitor);
|
||||
|
||||
virtual nsresult EvictEntries(const char * clientID);
|
||||
|
||||
bool EntryIsTooBig(int64_t entrySize);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
/**
|
||||
* Preference accessors
|
||||
*/
|
||||
void SetCacheParentDirectory(nsIFile * parentDir);
|
||||
void SetCapacity(uint32_t capacity);
|
||||
void SetMaxEntrySize(int32_t maxSizeInKilobytes);
|
||||
|
||||
/* private: */
|
||||
|
||||
void getCacheDirectory(nsIFile ** result);
|
||||
uint32_t getCacheCapacity();
|
||||
uint32_t getCacheSize();
|
||||
uint32_t getEntryCount();
|
||||
|
||||
nsDiskCacheMap * CacheMap() { return &mCacheMap; }
|
||||
|
||||
private:
|
||||
friend class nsDiskCacheDeviceDeactivateEntryEvent;
|
||||
friend class nsEvictDiskCacheEntriesEvent;
|
||||
friend class nsDiskCacheMap;
|
||||
/**
|
||||
* Private methods
|
||||
*/
|
||||
|
||||
inline bool IsValidBinding(nsDiskCacheBinding *binding)
|
||||
{
|
||||
NS_ASSERTION(binding, " binding == nullptr");
|
||||
NS_ASSERTION(binding->mDeactivateEvent == nullptr,
|
||||
" entry in process of deactivation");
|
||||
return (binding && !binding->mDeactivateEvent);
|
||||
}
|
||||
|
||||
bool Initialized() { return mInitialized; }
|
||||
|
||||
nsresult Shutdown_Private(bool flush);
|
||||
nsresult DeactivateEntry_Private(nsCacheEntry * entry,
|
||||
nsDiskCacheBinding * binding);
|
||||
|
||||
nsresult OpenDiskCache();
|
||||
nsresult ClearDiskCache();
|
||||
|
||||
nsresult EvictDiskCacheEntries(uint32_t targetCapacity);
|
||||
|
||||
/**
|
||||
* Member variables
|
||||
*/
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
nsDiskCacheBindery mBindery;
|
||||
uint32_t mCacheCapacity; // Unit is KiB's
|
||||
int32_t mMaxEntrySize; // Unit is bytes internally
|
||||
// XXX need soft/hard limits, currentTotal
|
||||
nsDiskCacheMap mCacheMap;
|
||||
bool mInitialized;
|
||||
bool mClearingDiskCache;
|
||||
};
|
||||
|
||||
#endif // _nsDiskCacheDevice_h_
|
||||
2906
netwerk/cache/nsDiskCacheDeviceSQL.cpp
vendored
Normal file
2906
netwerk/cache/nsDiskCacheDeviceSQL.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
290
netwerk/cache/nsDiskCacheDeviceSQL.h
vendored
Normal file
290
netwerk/cache/nsDiskCacheDeviceSQL.h
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et cin: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsOfflineCacheDevice_h__
|
||||
#define nsOfflineCacheDevice_h__
|
||||
|
||||
#include "nsCacheDevice.h"
|
||||
#include "nsIApplicationCache.h"
|
||||
#include "nsIApplicationCacheService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "mozIStorageFunction.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
class nsIURI;
|
||||
class nsOfflineCacheDevice;
|
||||
class mozIStorageService;
|
||||
class nsILoadContextInfo;
|
||||
namespace mozilla { class OriginAttributesPattern; }
|
||||
|
||||
class nsApplicationCacheNamespace final : public nsIApplicationCacheNamespace
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIAPPLICATIONCACHENAMESPACE
|
||||
|
||||
nsApplicationCacheNamespace() : mItemType(0) {}
|
||||
|
||||
private:
|
||||
~nsApplicationCacheNamespace() {}
|
||||
|
||||
uint32_t mItemType;
|
||||
nsCString mNamespaceSpec;
|
||||
nsCString mData;
|
||||
};
|
||||
|
||||
class nsOfflineCacheEvictionFunction final : public mozIStorageFunction {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGEFUNCTION
|
||||
|
||||
explicit nsOfflineCacheEvictionFunction(nsOfflineCacheDevice *device);
|
||||
|
||||
void Init();
|
||||
void Reset();
|
||||
void Apply();
|
||||
|
||||
private:
|
||||
~nsOfflineCacheEvictionFunction() {}
|
||||
|
||||
nsOfflineCacheDevice *mDevice;
|
||||
bool mTLSInited;
|
||||
};
|
||||
|
||||
class nsOfflineCacheDevice final : public nsCacheDevice
|
||||
, public nsISupports
|
||||
{
|
||||
public:
|
||||
nsOfflineCacheDevice();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
/**
|
||||
* nsCacheDevice methods
|
||||
*/
|
||||
|
||||
virtual nsresult Init() override;
|
||||
nsresult InitWithSqlite(mozIStorageService * ss);
|
||||
virtual nsresult Shutdown() override;
|
||||
|
||||
virtual const char * GetDeviceID(void) override;
|
||||
virtual nsCacheEntry * FindEntry(nsCString * key, bool *collision) override;
|
||||
virtual nsresult DeactivateEntry(nsCacheEntry * entry) override;
|
||||
virtual nsresult BindEntry(nsCacheEntry * entry) override;
|
||||
virtual void DoomEntry( nsCacheEntry * entry ) override;
|
||||
|
||||
virtual nsresult OpenInputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIInputStream ** result) override;
|
||||
|
||||
virtual nsresult OpenOutputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIOutputStream ** result) override;
|
||||
|
||||
virtual nsresult GetFileForEntry(nsCacheEntry * entry,
|
||||
nsIFile ** result) override;
|
||||
|
||||
virtual nsresult OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize) override;
|
||||
|
||||
virtual nsresult Visit(nsICacheVisitor * visitor) override;
|
||||
|
||||
virtual nsresult EvictEntries(const char * clientID) override;
|
||||
|
||||
/* Entry ownership */
|
||||
nsresult GetOwnerDomains(const char * clientID,
|
||||
uint32_t * count,
|
||||
char *** domains);
|
||||
nsresult GetOwnerURIs(const char * clientID,
|
||||
const nsACString & ownerDomain,
|
||||
uint32_t * count,
|
||||
char *** uris);
|
||||
nsresult SetOwnedKeys(const char * clientID,
|
||||
const nsACString & ownerDomain,
|
||||
const nsACString & ownerUrl,
|
||||
uint32_t count,
|
||||
const char ** keys);
|
||||
nsresult GetOwnedKeys(const char * clientID,
|
||||
const nsACString & ownerDomain,
|
||||
const nsACString & ownerUrl,
|
||||
uint32_t * count,
|
||||
char *** keys);
|
||||
nsresult AddOwnedKey(const char * clientID,
|
||||
const nsACString & ownerDomain,
|
||||
const nsACString & ownerURI,
|
||||
const nsACString & key);
|
||||
nsresult RemoveOwnedKey(const char * clientID,
|
||||
const nsACString & ownerDomain,
|
||||
const nsACString & ownerURI,
|
||||
const nsACString & key);
|
||||
nsresult KeyIsOwned(const char * clientID,
|
||||
const nsACString & ownerDomain,
|
||||
const nsACString & ownerURI,
|
||||
const nsACString & key,
|
||||
bool * isOwned);
|
||||
|
||||
nsresult ClearKeysOwnedByDomain(const char *clientID,
|
||||
const nsACString &ownerDomain);
|
||||
nsresult EvictUnownedEntries(const char *clientID);
|
||||
|
||||
static nsresult BuildApplicationCacheGroupID(nsIURI *aManifestURL,
|
||||
nsACString const &aOriginSuffix,
|
||||
nsACString &_result);
|
||||
|
||||
nsresult ActivateCache(const nsCSubstring &group,
|
||||
const nsCSubstring &clientID);
|
||||
bool IsActiveCache(const nsCSubstring &group,
|
||||
const nsCSubstring &clientID);
|
||||
nsresult CreateApplicationCache(const nsACString &group,
|
||||
nsIApplicationCache **out);
|
||||
|
||||
nsresult GetApplicationCache(const nsACString &clientID,
|
||||
nsIApplicationCache **out);
|
||||
nsresult GetApplicationCache_Unlocked(const nsACString &clientID,
|
||||
nsIApplicationCache **out);
|
||||
|
||||
nsresult GetActiveCache(const nsACString &group,
|
||||
nsIApplicationCache **out);
|
||||
|
||||
nsresult DeactivateGroup(const nsACString &group);
|
||||
|
||||
nsresult ChooseApplicationCache(const nsACString &key,
|
||||
nsILoadContextInfo *loadContext,
|
||||
nsIApplicationCache **out);
|
||||
|
||||
nsresult CacheOpportunistically(nsIApplicationCache* cache,
|
||||
const nsACString &key);
|
||||
|
||||
nsresult Evict(nsILoadContextInfo *aInfo);
|
||||
nsresult Evict(mozilla::OriginAttributesPattern const &aPattern);
|
||||
|
||||
nsresult GetGroups(uint32_t *count,char ***keys);
|
||||
|
||||
nsresult GetGroupsTimeOrdered(uint32_t *count,
|
||||
char ***keys);
|
||||
|
||||
bool IsLocked(const nsACString &key);
|
||||
void Lock(const nsACString &key);
|
||||
void Unlock(const nsACString &key);
|
||||
|
||||
/**
|
||||
* Preference accessors
|
||||
*/
|
||||
|
||||
void SetCacheParentDirectory(nsIFile * parentDir);
|
||||
void SetCapacity(uint32_t capacity);
|
||||
void SetAutoShutdown() { mAutoShutdown = true; }
|
||||
bool AutoShutdown(nsIApplicationCache * aAppCache);
|
||||
|
||||
nsIFile * BaseDirectory() { return mBaseDirectory; }
|
||||
nsIFile * CacheDirectory() { return mCacheDirectory; }
|
||||
uint32_t CacheCapacity() { return mCacheCapacity; }
|
||||
uint32_t CacheSize();
|
||||
uint32_t EntryCount();
|
||||
|
||||
private:
|
||||
~nsOfflineCacheDevice();
|
||||
|
||||
friend class nsApplicationCache;
|
||||
|
||||
static bool GetStrictFileOriginPolicy();
|
||||
|
||||
bool Initialized() { return mDB != nullptr; }
|
||||
|
||||
nsresult InitActiveCaches();
|
||||
nsresult UpdateEntry(nsCacheEntry *entry);
|
||||
nsresult UpdateEntrySize(nsCacheEntry *entry, uint32_t newSize);
|
||||
nsresult DeleteEntry(nsCacheEntry *entry, bool deleteData);
|
||||
nsresult DeleteData(nsCacheEntry *entry);
|
||||
nsresult EnableEvictionObserver();
|
||||
nsresult DisableEvictionObserver();
|
||||
|
||||
bool CanUseCache(nsIURI *keyURI, const nsACString &clientID, nsILoadContextInfo *loadContext);
|
||||
|
||||
nsresult MarkEntry(const nsCString &clientID,
|
||||
const nsACString &key,
|
||||
uint32_t typeBits);
|
||||
nsresult UnmarkEntry(const nsCString &clientID,
|
||||
const nsACString &key,
|
||||
uint32_t typeBits);
|
||||
|
||||
nsresult CacheOpportunistically(const nsCString &clientID,
|
||||
const nsACString &key);
|
||||
nsresult GetTypes(const nsCString &clientID,
|
||||
const nsACString &key,
|
||||
uint32_t *typeBits);
|
||||
|
||||
nsresult GetMatchingNamespace(const nsCString &clientID,
|
||||
const nsACString &key,
|
||||
nsIApplicationCacheNamespace **out);
|
||||
nsresult GatherEntries(const nsCString &clientID,
|
||||
uint32_t typeBits,
|
||||
uint32_t *count,
|
||||
char *** values);
|
||||
nsresult AddNamespace(const nsCString &clientID,
|
||||
nsIApplicationCacheNamespace *ns);
|
||||
|
||||
nsresult GetUsage(const nsACString &clientID,
|
||||
uint32_t *usage);
|
||||
|
||||
nsresult RunSimpleQuery(mozIStorageStatement *statment,
|
||||
uint32_t resultIndex,
|
||||
uint32_t * count,
|
||||
char *** values);
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> mDB;
|
||||
RefPtr<nsOfflineCacheEvictionFunction> mEvictionFunction;
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_CacheSize;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_ApplicationCacheSize;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_EntryCount;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_UpdateEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_UpdateEntrySize;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_DeleteEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_FindEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_BindEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_ClearDomain;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_MarkEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_UnmarkEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_GetTypes;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_FindNamespaceEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_InsertNamespaceEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_CleanupUnmarked;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_GatherEntries;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_ActivateClient;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_DeactivateGroup;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_FindClient;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_FindClientByNamespace;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_EnumerateApps;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_EnumerateGroups;
|
||||
nsCOMPtr<mozIStorageStatement> mStatement_EnumerateGroupsTimeOrder;
|
||||
|
||||
nsCOMPtr<nsIFile> mBaseDirectory;
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
uint32_t mCacheCapacity; // in bytes
|
||||
int32_t mDeltaCounter;
|
||||
bool mAutoShutdown;
|
||||
|
||||
mozilla::Mutex mLock;
|
||||
|
||||
nsInterfaceHashtable<nsCStringHashKey, nsIWeakReference> mCaches;
|
||||
nsClassHashtable<nsCStringHashKey, nsCString> mActiveCachesByGroup;
|
||||
nsTHashtable<nsCStringHashKey> mActiveCaches;
|
||||
nsTHashtable<nsCStringHashKey> mLockedEntries;
|
||||
|
||||
nsCOMPtr<nsIThread> mInitThread;
|
||||
};
|
||||
|
||||
#endif // nsOfflineCacheDevice_h__
|
||||
133
netwerk/cache/nsDiskCacheEntry.cpp
vendored
Normal file
133
netwerk/cache/nsDiskCacheEntry.cpp
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCache.h"
|
||||
#include "nsDiskCache.h"
|
||||
#include "nsDiskCacheEntry.h"
|
||||
#include "nsDiskCacheBinding.h"
|
||||
#include "nsCRT.h"
|
||||
|
||||
#include "nsISerializable.h"
|
||||
#include "nsSerializationHelper.h"
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheEntry
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* CreateCacheEntry()
|
||||
*
|
||||
* Creates an nsCacheEntry and sets all fields except for the binding.
|
||||
*/
|
||||
nsCacheEntry *
|
||||
nsDiskCacheEntry::CreateCacheEntry(nsCacheDevice * device)
|
||||
{
|
||||
nsCacheEntry * entry = nullptr;
|
||||
nsresult rv = nsCacheEntry::Create(Key(),
|
||||
nsICache::STREAM_BASED,
|
||||
nsICache::STORE_ON_DISK,
|
||||
device,
|
||||
&entry);
|
||||
if (NS_FAILED(rv) || !entry) return nullptr;
|
||||
|
||||
entry->SetFetchCount(mFetchCount);
|
||||
entry->SetLastFetched(mLastFetched);
|
||||
entry->SetLastModified(mLastModified);
|
||||
entry->SetExpirationTime(mExpirationTime);
|
||||
entry->SetCacheDevice(device);
|
||||
// XXX why does nsCacheService have to fill out device in BindEntry()?
|
||||
entry->SetDataSize(mDataSize);
|
||||
|
||||
rv = entry->UnflattenMetaData(MetaData(), mMetaDataSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
delete entry;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Restore security info, if present
|
||||
const char* info = entry->GetMetaDataElement("security-info");
|
||||
if (info) {
|
||||
nsCOMPtr<nsISupports> infoObj;
|
||||
rv = NS_DeserializeObject(nsDependentCString(info),
|
||||
getter_AddRefs(infoObj));
|
||||
if (NS_FAILED(rv)) {
|
||||
delete entry;
|
||||
return nullptr;
|
||||
}
|
||||
entry->SetSecurityInfo(infoObj);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheEntryInfo
|
||||
*****************************************************************************/
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDiskCacheEntryInfo, nsICacheEntryInfo)
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetClientID(char ** clientID)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(clientID);
|
||||
return ClientIDFromCacheKey(nsDependentCString(mDiskEntry->Key()), clientID);
|
||||
}
|
||||
|
||||
extern const char DISK_CACHE_DEVICE_ID[];
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetDeviceID(char ** deviceID)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(deviceID);
|
||||
*deviceID = NS_strdup(mDeviceID);
|
||||
return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetKey(nsACString &clientKey)
|
||||
{
|
||||
return ClientKeyFromCacheKey(nsDependentCString(mDiskEntry->Key()), clientKey);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetFetchCount(int32_t *aFetchCount)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aFetchCount);
|
||||
*aFetchCount = mDiskEntry->mFetchCount;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetLastFetched(uint32_t *aLastFetched)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLastFetched);
|
||||
*aLastFetched = mDiskEntry->mLastFetched;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetLastModified(uint32_t *aLastModified)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLastModified);
|
||||
*aLastModified = mDiskEntry->mLastModified;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetExpirationTime(uint32_t *aExpirationTime)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aExpirationTime);
|
||||
*aExpirationTime = mDiskEntry->mExpirationTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::IsStreamBased(bool *aStreamBased)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aStreamBased);
|
||||
*aStreamBased = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDiskCacheEntryInfo::GetDataSize(uint32_t *aDataSize)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDataSize);
|
||||
*aDataSize = mDiskEntry->mDataSize;
|
||||
return NS_OK;
|
||||
}
|
||||
100
netwerk/cache/nsDiskCacheEntry.h
vendored
Normal file
100
netwerk/cache/nsDiskCacheEntry.h
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsDiskCacheEntry_h_
|
||||
#define _nsDiskCacheEntry_h_
|
||||
|
||||
#include "nsDiskCacheMap.h"
|
||||
|
||||
#include "nsCacheEntry.h"
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheEntry
|
||||
*****************************************************************************/
|
||||
struct nsDiskCacheEntry {
|
||||
uint32_t mHeaderVersion; // useful for stand-alone metadata files
|
||||
uint32_t mMetaLocation; // for verification
|
||||
int32_t mFetchCount;
|
||||
uint32_t mLastFetched;
|
||||
uint32_t mLastModified;
|
||||
uint32_t mExpirationTime;
|
||||
uint32_t mDataSize;
|
||||
uint32_t mKeySize; // includes terminating null byte
|
||||
uint32_t mMetaDataSize; // includes terminating null byte
|
||||
// followed by key data (mKeySize bytes)
|
||||
// followed by meta data (mMetaDataSize bytes)
|
||||
|
||||
uint32_t Size() { return sizeof(nsDiskCacheEntry) +
|
||||
mKeySize + mMetaDataSize;
|
||||
}
|
||||
|
||||
char* Key() { return reinterpret_cast<char*const>(this) +
|
||||
sizeof(nsDiskCacheEntry);
|
||||
}
|
||||
|
||||
char* MetaData()
|
||||
{ return Key() + mKeySize; }
|
||||
|
||||
nsCacheEntry * CreateCacheEntry(nsCacheDevice * device);
|
||||
|
||||
void Swap() // host to network (memory to disk)
|
||||
{
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
mHeaderVersion = htonl(mHeaderVersion);
|
||||
mMetaLocation = htonl(mMetaLocation);
|
||||
mFetchCount = htonl(mFetchCount);
|
||||
mLastFetched = htonl(mLastFetched);
|
||||
mLastModified = htonl(mLastModified);
|
||||
mExpirationTime = htonl(mExpirationTime);
|
||||
mDataSize = htonl(mDataSize);
|
||||
mKeySize = htonl(mKeySize);
|
||||
mMetaDataSize = htonl(mMetaDataSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Unswap() // network to host (disk to memory)
|
||||
{
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
mHeaderVersion = ntohl(mHeaderVersion);
|
||||
mMetaLocation = ntohl(mMetaLocation);
|
||||
mFetchCount = ntohl(mFetchCount);
|
||||
mLastFetched = ntohl(mLastFetched);
|
||||
mLastModified = ntohl(mLastModified);
|
||||
mExpirationTime = ntohl(mExpirationTime);
|
||||
mDataSize = ntohl(mDataSize);
|
||||
mKeySize = ntohl(mKeySize);
|
||||
mMetaDataSize = ntohl(mMetaDataSize);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheEntryInfo
|
||||
*****************************************************************************/
|
||||
class nsDiskCacheEntryInfo : public nsICacheEntryInfo {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICACHEENTRYINFO
|
||||
|
||||
nsDiskCacheEntryInfo(const char * deviceID, nsDiskCacheEntry * diskEntry)
|
||||
: mDeviceID(deviceID)
|
||||
, mDiskEntry(diskEntry)
|
||||
{
|
||||
}
|
||||
|
||||
const char* Key() { return mDiskEntry->Key(); }
|
||||
|
||||
private:
|
||||
virtual ~nsDiskCacheEntryInfo() {}
|
||||
|
||||
const char * mDeviceID;
|
||||
nsDiskCacheEntry * mDiskEntry;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _nsDiskCacheEntry_h_ */
|
||||
1430
netwerk/cache/nsDiskCacheMap.cpp
vendored
Normal file
1430
netwerk/cache/nsDiskCacheMap.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
577
netwerk/cache/nsDiskCacheMap.h
vendored
Normal file
577
netwerk/cache/nsDiskCacheMap.h
vendored
Normal file
|
|
@ -0,0 +1,577 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 cin et: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsDiskCacheMap_h_
|
||||
#define _nsDiskCacheMap_h_
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include <limits.h>
|
||||
|
||||
#include "prnetdb.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
#include "nsDiskCache.h"
|
||||
#include "nsDiskCacheBlockFile.h"
|
||||
|
||||
|
||||
class nsDiskCacheBinding;
|
||||
struct nsDiskCacheEntry;
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheRecord
|
||||
*
|
||||
* Cache Location Format
|
||||
*
|
||||
* 1000 0000 0000 0000 0000 0000 0000 0000 : initialized bit
|
||||
*
|
||||
* 0011 0000 0000 0000 0000 0000 0000 0000 : File Selector (0 = separate file)
|
||||
* 0000 0011 0000 0000 0000 0000 0000 0000 : number of extra contiguous blocks 1-4
|
||||
* 0100 1100 0000 0000 0000 0000 0000 0000 : reserved bits
|
||||
* 0000 0000 1111 1111 1111 1111 1111 1111 : block# 0-16777216 (2^24)
|
||||
*
|
||||
* 0000 0000 1111 1111 1111 1111 0000 0000 : eFileSizeMask (size of file in k: see note)
|
||||
* 0000 0000 0000 0000 0000 0000 1111 1111 : eFileGenerationMask
|
||||
*
|
||||
* File Selector:
|
||||
* 0 = separate file on disk
|
||||
* 1 = 256 byte block file
|
||||
* 2 = 1k block file
|
||||
* 3 = 4k block file
|
||||
*
|
||||
* eFileSizeMask note: Files larger than 65535 KiB have this limit stored in
|
||||
* the location. The file itself must be examined to
|
||||
* determine its actual size if necessary.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
We have 3 block files with roughly the same max size (32MB)
|
||||
1 - block size 256B, number of blocks 131072
|
||||
2 - block size 1kB, number of blocks 32768
|
||||
3 - block size 4kB, number of blocks 8192
|
||||
*/
|
||||
#define kNumBlockFiles 3
|
||||
#define SIZE_SHIFT(idx) (2 * ((idx) - 1))
|
||||
#define BLOCK_SIZE_FOR_INDEX(idx) ((idx) ? (256 << SIZE_SHIFT(idx)) : 0)
|
||||
#define BITMAP_SIZE_FOR_INDEX(idx) ((idx) ? (131072 >> SIZE_SHIFT(idx)) : 0)
|
||||
|
||||
// Min and max values for the number of records in the DiskCachemap
|
||||
#define kMinRecordCount 512
|
||||
|
||||
#define kSeparateFile 0
|
||||
#define kBuckets (1 << 5) // must be a power of 2!
|
||||
|
||||
// Maximum size in K which can be stored in the location (see eFileSizeMask).
|
||||
// Both data and metadata can be larger, but only up to kMaxDataSizeK can be
|
||||
// counted into total cache size. I.e. if there are entries where either data or
|
||||
// metadata is larger than kMaxDataSizeK, the total cache size will be
|
||||
// inaccurate (smaller) than the actual cache size. The alternative is to stat
|
||||
// the files to find the real size, which was decided against for performance
|
||||
// reasons. See bug #651100 comment #21.
|
||||
#define kMaxDataSizeK 0xFFFF
|
||||
|
||||
// preallocate up to 1MB of separate cache file
|
||||
#define kPreallocateLimit 1 * 1024 * 1024
|
||||
|
||||
// The minimum amount of milliseconds to wait before re-attempting to
|
||||
// revalidate the cache.
|
||||
#define kRevalidateCacheTimeout 3000
|
||||
#define kRevalidateCacheTimeoutTolerance 10
|
||||
#define kRevalidateCacheErrorTimeout 1000
|
||||
|
||||
class nsDiskCacheRecord {
|
||||
|
||||
private:
|
||||
uint32_t mHashNumber;
|
||||
uint32_t mEvictionRank;
|
||||
uint32_t mDataLocation;
|
||||
uint32_t mMetaLocation;
|
||||
|
||||
enum {
|
||||
eLocationInitializedMask = 0x80000000,
|
||||
|
||||
eLocationSelectorMask = 0x30000000,
|
||||
eLocationSelectorOffset = 28,
|
||||
|
||||
eExtraBlocksMask = 0x03000000,
|
||||
eExtraBlocksOffset = 24,
|
||||
|
||||
eReservedMask = 0x4C000000,
|
||||
|
||||
eBlockNumberMask = 0x00FFFFFF,
|
||||
|
||||
eFileSizeMask = 0x00FFFF00,
|
||||
eFileSizeOffset = 8,
|
||||
eFileGenerationMask = 0x000000FF,
|
||||
eFileReservedMask = 0x4F000000
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
nsDiskCacheRecord()
|
||||
: mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool ValidRecord()
|
||||
{
|
||||
if ((mDataLocation & eReservedMask) || (mMetaLocation & eReservedMask))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// HashNumber accessors
|
||||
uint32_t HashNumber() const { return mHashNumber; }
|
||||
void SetHashNumber( uint32_t hashNumber) { mHashNumber = hashNumber; }
|
||||
|
||||
// EvictionRank accessors
|
||||
uint32_t EvictionRank() const { return mEvictionRank; }
|
||||
void SetEvictionRank( uint32_t rank) { mEvictionRank = rank ? rank : 1; }
|
||||
|
||||
// DataLocation accessors
|
||||
bool DataLocationInitialized() const { return 0 != (mDataLocation & eLocationInitializedMask); }
|
||||
void ClearDataLocation() { mDataLocation = 0; }
|
||||
|
||||
uint32_t DataFile() const
|
||||
{
|
||||
return (uint32_t)(mDataLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
|
||||
}
|
||||
|
||||
void SetDataBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
|
||||
{
|
||||
// clear everything
|
||||
mDataLocation = 0;
|
||||
|
||||
// set file index
|
||||
NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
|
||||
NS_ASSERTION( index > 0,"invalid location index");
|
||||
mDataLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
|
||||
|
||||
// set startBlock
|
||||
NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
|
||||
mDataLocation |= startBlock & eBlockNumberMask;
|
||||
|
||||
// set blockCount
|
||||
NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
|
||||
--blockCount;
|
||||
mDataLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
|
||||
|
||||
mDataLocation |= eLocationInitializedMask;
|
||||
}
|
||||
|
||||
uint32_t DataBlockCount() const
|
||||
{
|
||||
return (uint32_t)((mDataLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
|
||||
}
|
||||
|
||||
uint32_t DataStartBlock() const
|
||||
{
|
||||
return (mDataLocation & eBlockNumberMask);
|
||||
}
|
||||
|
||||
uint32_t DataBlockSize() const
|
||||
{
|
||||
return BLOCK_SIZE_FOR_INDEX(DataFile());
|
||||
}
|
||||
|
||||
uint32_t DataFileSize() const { return (mDataLocation & eFileSizeMask) >> eFileSizeOffset; }
|
||||
void SetDataFileSize(uint32_t size)
|
||||
{
|
||||
NS_ASSERTION((mDataLocation & eFileReservedMask) == 0, "bad location");
|
||||
mDataLocation &= ~eFileSizeMask; // clear eFileSizeMask
|
||||
mDataLocation |= (size << eFileSizeOffset) & eFileSizeMask;
|
||||
}
|
||||
|
||||
uint8_t DataFileGeneration() const
|
||||
{
|
||||
return (mDataLocation & eFileGenerationMask);
|
||||
}
|
||||
|
||||
void SetDataFileGeneration( uint8_t generation)
|
||||
{
|
||||
// clear everything, (separate file index = 0)
|
||||
mDataLocation = 0;
|
||||
mDataLocation |= generation & eFileGenerationMask;
|
||||
mDataLocation |= eLocationInitializedMask;
|
||||
}
|
||||
|
||||
// MetaLocation accessors
|
||||
bool MetaLocationInitialized() const { return 0 != (mMetaLocation & eLocationInitializedMask); }
|
||||
void ClearMetaLocation() { mMetaLocation = 0; }
|
||||
uint32_t MetaLocation() const { return mMetaLocation; }
|
||||
|
||||
uint32_t MetaFile() const
|
||||
{
|
||||
return (uint32_t)(mMetaLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
|
||||
}
|
||||
|
||||
void SetMetaBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
|
||||
{
|
||||
// clear everything
|
||||
mMetaLocation = 0;
|
||||
|
||||
// set file index
|
||||
NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
|
||||
NS_ASSERTION( index > 0, "invalid location index");
|
||||
mMetaLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
|
||||
|
||||
// set startBlock
|
||||
NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
|
||||
mMetaLocation |= startBlock & eBlockNumberMask;
|
||||
|
||||
// set blockCount
|
||||
NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
|
||||
--blockCount;
|
||||
mMetaLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
|
||||
|
||||
mMetaLocation |= eLocationInitializedMask;
|
||||
}
|
||||
|
||||
uint32_t MetaBlockCount() const
|
||||
{
|
||||
return (uint32_t)((mMetaLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
|
||||
}
|
||||
|
||||
uint32_t MetaStartBlock() const
|
||||
{
|
||||
return (mMetaLocation & eBlockNumberMask);
|
||||
}
|
||||
|
||||
uint32_t MetaBlockSize() const
|
||||
{
|
||||
return BLOCK_SIZE_FOR_INDEX(MetaFile());
|
||||
}
|
||||
|
||||
uint32_t MetaFileSize() const { return (mMetaLocation & eFileSizeMask) >> eFileSizeOffset; }
|
||||
void SetMetaFileSize(uint32_t size)
|
||||
{
|
||||
mMetaLocation &= ~eFileSizeMask; // clear eFileSizeMask
|
||||
mMetaLocation |= (size << eFileSizeOffset) & eFileSizeMask;
|
||||
}
|
||||
|
||||
uint8_t MetaFileGeneration() const
|
||||
{
|
||||
return (mMetaLocation & eFileGenerationMask);
|
||||
}
|
||||
|
||||
void SetMetaFileGeneration( uint8_t generation)
|
||||
{
|
||||
// clear everything, (separate file index = 0)
|
||||
mMetaLocation = 0;
|
||||
mMetaLocation |= generation & eFileGenerationMask;
|
||||
mMetaLocation |= eLocationInitializedMask;
|
||||
}
|
||||
|
||||
uint8_t Generation() const
|
||||
{
|
||||
if ((mDataLocation & eLocationInitializedMask) &&
|
||||
(DataFile() == 0))
|
||||
return DataFileGeneration();
|
||||
|
||||
if ((mMetaLocation & eLocationInitializedMask) &&
|
||||
(MetaFile() == 0))
|
||||
return MetaFileGeneration();
|
||||
|
||||
return 0; // no generation
|
||||
}
|
||||
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
void Swap()
|
||||
{
|
||||
mHashNumber = htonl(mHashNumber);
|
||||
mEvictionRank = htonl(mEvictionRank);
|
||||
mDataLocation = htonl(mDataLocation);
|
||||
mMetaLocation = htonl(mMetaLocation);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
void Unswap()
|
||||
{
|
||||
mHashNumber = ntohl(mHashNumber);
|
||||
mEvictionRank = ntohl(mEvictionRank);
|
||||
mDataLocation = ntohl(mDataLocation);
|
||||
mMetaLocation = ntohl(mMetaLocation);
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheRecordVisitor
|
||||
*****************************************************************************/
|
||||
|
||||
enum { kDeleteRecordAndContinue = -1,
|
||||
kStopVisitingRecords = 0,
|
||||
kVisitNextRecord = 1
|
||||
};
|
||||
|
||||
class nsDiskCacheRecordVisitor {
|
||||
public:
|
||||
|
||||
virtual int32_t VisitRecord( nsDiskCacheRecord * mapRecord) = 0;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheHeader
|
||||
*****************************************************************************/
|
||||
|
||||
struct nsDiskCacheHeader {
|
||||
uint32_t mVersion; // cache version.
|
||||
uint32_t mDataSize; // size of cache in units of 1024bytes.
|
||||
int32_t mEntryCount; // number of entries stored in cache.
|
||||
uint32_t mIsDirty; // dirty flag.
|
||||
int32_t mRecordCount; // Number of records
|
||||
uint32_t mEvictionRank[kBuckets]; // Highest EvictionRank of the bucket
|
||||
uint32_t mBucketUsage[kBuckets]; // Number of used entries in the bucket
|
||||
|
||||
nsDiskCacheHeader()
|
||||
: mVersion(nsDiskCache::kCurrentVersion)
|
||||
, mDataSize(0)
|
||||
, mEntryCount(0)
|
||||
, mIsDirty(true)
|
||||
, mRecordCount(0)
|
||||
{}
|
||||
|
||||
void Swap()
|
||||
{
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
mVersion = htonl(mVersion);
|
||||
mDataSize = htonl(mDataSize);
|
||||
mEntryCount = htonl(mEntryCount);
|
||||
mIsDirty = htonl(mIsDirty);
|
||||
mRecordCount = htonl(mRecordCount);
|
||||
|
||||
for (uint32_t i = 0; i < kBuckets ; i++) {
|
||||
mEvictionRank[i] = htonl(mEvictionRank[i]);
|
||||
mBucketUsage[i] = htonl(mBucketUsage[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Unswap()
|
||||
{
|
||||
#if defined(IS_LITTLE_ENDIAN)
|
||||
mVersion = ntohl(mVersion);
|
||||
mDataSize = ntohl(mDataSize);
|
||||
mEntryCount = ntohl(mEntryCount);
|
||||
mIsDirty = ntohl(mIsDirty);
|
||||
mRecordCount = ntohl(mRecordCount);
|
||||
|
||||
for (uint32_t i = 0; i < kBuckets ; i++) {
|
||||
mEvictionRank[i] = ntohl(mEvictionRank[i]);
|
||||
mBucketUsage[i] = ntohl(mBucketUsage[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheMap
|
||||
*****************************************************************************/
|
||||
|
||||
class nsDiskCacheMap {
|
||||
public:
|
||||
|
||||
nsDiskCacheMap() :
|
||||
mCacheDirectory(nullptr),
|
||||
mMapFD(nullptr),
|
||||
mCleanFD(nullptr),
|
||||
mRecordArray(nullptr),
|
||||
mBufferSize(0),
|
||||
mBuffer(nullptr),
|
||||
mMaxRecordCount(16384), // this default value won't matter
|
||||
mIsDirtyCacheFlushed(false),
|
||||
mLastInvalidateTime(0)
|
||||
{ }
|
||||
|
||||
~nsDiskCacheMap()
|
||||
{
|
||||
(void) Close(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* File Operations
|
||||
*
|
||||
* Open
|
||||
*
|
||||
* Creates a new cache map file if one doesn't exist.
|
||||
* Returns error if it detects change in format or cache wasn't closed.
|
||||
*/
|
||||
nsresult Open( nsIFile * cacheDirectory,
|
||||
nsDiskCache::CorruptCacheInfo * corruptInfo);
|
||||
nsresult Close(bool flush);
|
||||
nsresult Trim();
|
||||
|
||||
nsresult FlushHeader();
|
||||
nsresult FlushRecords( bool unswap);
|
||||
|
||||
void NotifyCapacityChange(uint32_t capacity);
|
||||
|
||||
/**
|
||||
* Record operations
|
||||
*/
|
||||
nsresult AddRecord( nsDiskCacheRecord * mapRecord, nsDiskCacheRecord * oldRecord);
|
||||
nsresult UpdateRecord( nsDiskCacheRecord * mapRecord);
|
||||
nsresult FindRecord( uint32_t hashNumber, nsDiskCacheRecord * mapRecord);
|
||||
nsresult DeleteRecord( nsDiskCacheRecord * mapRecord);
|
||||
nsresult VisitRecords( nsDiskCacheRecordVisitor * visitor);
|
||||
nsresult EvictRecords( nsDiskCacheRecordVisitor * visitor);
|
||||
|
||||
/**
|
||||
* Disk Entry operations
|
||||
*/
|
||||
nsresult DeleteStorage( nsDiskCacheRecord * record);
|
||||
|
||||
nsresult GetFileForDiskCacheRecord( nsDiskCacheRecord * record,
|
||||
bool meta,
|
||||
bool createPath,
|
||||
nsIFile ** result);
|
||||
|
||||
nsresult GetLocalFileForDiskCacheRecord( nsDiskCacheRecord * record,
|
||||
bool meta,
|
||||
bool createPath,
|
||||
nsIFile ** result);
|
||||
|
||||
// On success, this returns the buffer owned by nsDiskCacheMap,
|
||||
// so it must not be deleted by the caller.
|
||||
nsDiskCacheEntry * ReadDiskCacheEntry( nsDiskCacheRecord * record);
|
||||
|
||||
nsresult WriteDiskCacheEntry( nsDiskCacheBinding * binding);
|
||||
|
||||
nsresult ReadDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
|
||||
nsresult WriteDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
|
||||
nsresult DeleteStorage( nsDiskCacheRecord * record, bool metaData);
|
||||
|
||||
/**
|
||||
* Statistical Operations
|
||||
*/
|
||||
void IncrementTotalSize( uint32_t delta)
|
||||
{
|
||||
mHeader.mDataSize += delta;
|
||||
mHeader.mIsDirty = true;
|
||||
}
|
||||
|
||||
void DecrementTotalSize( uint32_t delta)
|
||||
{
|
||||
NS_ASSERTION(mHeader.mDataSize >= delta, "disk cache size negative?");
|
||||
mHeader.mDataSize = mHeader.mDataSize > delta ? mHeader.mDataSize - delta : 0;
|
||||
mHeader.mIsDirty = true;
|
||||
}
|
||||
|
||||
inline void IncrementTotalSize( uint32_t blocks, uint32_t blockSize)
|
||||
{
|
||||
// Round up to nearest K
|
||||
IncrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
|
||||
}
|
||||
|
||||
inline void DecrementTotalSize( uint32_t blocks, uint32_t blockSize)
|
||||
{
|
||||
// Round up to nearest K
|
||||
DecrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
|
||||
}
|
||||
|
||||
uint32_t TotalSize() { return mHeader.mDataSize; }
|
||||
|
||||
int32_t EntryCount() { return mHeader.mEntryCount; }
|
||||
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Private methods
|
||||
*/
|
||||
nsresult OpenBlockFiles(nsDiskCache::CorruptCacheInfo * corruptInfo);
|
||||
nsresult CloseBlockFiles(bool flush);
|
||||
bool CacheFilesExist();
|
||||
|
||||
nsresult CreateCacheSubDirectories();
|
||||
|
||||
uint32_t CalculateFileIndex(uint32_t size);
|
||||
|
||||
nsresult GetBlockFileForIndex( uint32_t index, nsIFile ** result);
|
||||
uint32_t GetBlockSizeForIndex( uint32_t index) const {
|
||||
return BLOCK_SIZE_FOR_INDEX(index);
|
||||
}
|
||||
uint32_t GetBitMapSizeForIndex( uint32_t index) const {
|
||||
return BITMAP_SIZE_FOR_INDEX(index);
|
||||
}
|
||||
|
||||
// returns the bucket number
|
||||
uint32_t GetBucketIndex( uint32_t hashNumber) const {
|
||||
return (hashNumber & (kBuckets - 1));
|
||||
}
|
||||
|
||||
// Gets the size of the bucket (in number of records)
|
||||
uint32_t GetRecordsPerBucket() const {
|
||||
return mHeader.mRecordCount / kBuckets;
|
||||
}
|
||||
|
||||
// Gets the first record in the bucket
|
||||
nsDiskCacheRecord *GetFirstRecordInBucket(uint32_t bucket) const {
|
||||
return mRecordArray + bucket * GetRecordsPerBucket();
|
||||
}
|
||||
|
||||
uint32_t GetBucketRank(uint32_t bucketIndex, uint32_t targetRank);
|
||||
|
||||
int32_t VisitEachRecord(uint32_t bucketIndex,
|
||||
nsDiskCacheRecordVisitor * visitor,
|
||||
uint32_t evictionRank);
|
||||
|
||||
nsresult GrowRecords();
|
||||
nsresult ShrinkRecords();
|
||||
|
||||
nsresult EnsureBuffer(uint32_t bufSize);
|
||||
|
||||
// The returned structure will point to the buffer owned by nsDiskCacheMap,
|
||||
// so it must not be deleted by the caller.
|
||||
nsDiskCacheEntry * CreateDiskCacheEntry(nsDiskCacheBinding * binding,
|
||||
uint32_t * size);
|
||||
|
||||
// Initializes the _CACHE_CLEAN_ related functionality
|
||||
nsresult InitCacheClean(nsIFile * cacheDirectory,
|
||||
nsDiskCache::CorruptCacheInfo * corruptInfo);
|
||||
// Writes out a value of '0' or '1' in the _CACHE_CLEAN_ file
|
||||
nsresult WriteCacheClean(bool clean);
|
||||
// Resets the timout for revalidating the cache
|
||||
nsresult ResetCacheTimer(int32_t timeout = kRevalidateCacheTimeout);
|
||||
// Invalidates the cache, calls WriteCacheClean and ResetCacheTimer
|
||||
nsresult InvalidateCache();
|
||||
// Determines if the cache is in a safe state
|
||||
bool IsCacheInSafeState();
|
||||
// Revalidates the cache by writting out the header, records, and finally
|
||||
// by calling WriteCacheClean(true).
|
||||
nsresult RevalidateCache();
|
||||
// Timer which revalidates the cache
|
||||
static void RevalidateTimerCallback(nsITimer *aTimer, void *arg);
|
||||
|
||||
/**
|
||||
* data members
|
||||
*/
|
||||
private:
|
||||
nsCOMPtr<nsITimer> mCleanCacheTimer;
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
PRFileDesc * mMapFD;
|
||||
PRFileDesc * mCleanFD;
|
||||
nsDiskCacheRecord * mRecordArray;
|
||||
nsDiskCacheBlockFile mBlockFile[kNumBlockFiles];
|
||||
uint32_t mBufferSize;
|
||||
char * mBuffer;
|
||||
nsDiskCacheHeader mHeader;
|
||||
int32_t mMaxRecordCount;
|
||||
bool mIsDirtyCacheFlushed;
|
||||
PRIntervalTime mLastInvalidateTime;
|
||||
};
|
||||
|
||||
#endif // _nsDiskCacheMap_h_
|
||||
693
netwerk/cache/nsDiskCacheStreams.cpp
vendored
Normal file
693
netwerk/cache/nsDiskCacheStreams.cpp
vendored
Normal file
|
|
@ -0,0 +1,693 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "nsCache.h"
|
||||
#include "nsDiskCache.h"
|
||||
#include "nsDiskCacheDevice.h"
|
||||
#include "nsDiskCacheStreams.h"
|
||||
#include "nsCacheService.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include <algorithm>
|
||||
|
||||
// we pick 16k as the max buffer size because that is the threshold above which
|
||||
// we are unable to store the data in the cache block files
|
||||
// see nsDiskCacheMap.[cpp,h]
|
||||
#define kMaxBufferSize (16 * 1024)
|
||||
|
||||
// Assumptions:
|
||||
// - cache descriptors live for life of streams
|
||||
// - streams will only be used by FileTransport,
|
||||
// they will not be directly accessible to clients
|
||||
// - overlapped I/O is NOT supported
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheInputStream
|
||||
*****************************************************************************/
|
||||
class nsDiskCacheInputStream : public nsIInputStream {
|
||||
|
||||
public:
|
||||
|
||||
nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
|
||||
PRFileDesc * fileDesc,
|
||||
const char * buffer,
|
||||
uint32_t endOfStream);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
|
||||
private:
|
||||
virtual ~nsDiskCacheInputStream();
|
||||
|
||||
nsDiskCacheStreamIO * mStreamIO; // backpointer to parent
|
||||
PRFileDesc * mFD;
|
||||
const char * mBuffer;
|
||||
uint32_t mStreamEnd;
|
||||
uint32_t mPos; // stream position
|
||||
bool mClosed;
|
||||
};
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDiskCacheInputStream, nsIInputStream)
|
||||
|
||||
|
||||
nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
|
||||
PRFileDesc * fileDesc,
|
||||
const char * buffer,
|
||||
uint32_t endOfStream)
|
||||
: mStreamIO(parent)
|
||||
, mFD(fileDesc)
|
||||
, mBuffer(buffer)
|
||||
, mStreamEnd(endOfStream)
|
||||
, mPos(0)
|
||||
, mClosed(false)
|
||||
{
|
||||
NS_ADDREF(mStreamIO);
|
||||
mStreamIO->IncrementInputStreamCount();
|
||||
}
|
||||
|
||||
|
||||
nsDiskCacheInputStream::~nsDiskCacheInputStream()
|
||||
{
|
||||
Close();
|
||||
mStreamIO->DecrementInputStreamCount();
|
||||
NS_RELEASE(mStreamIO);
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheInputStream::Close()
|
||||
{
|
||||
if (!mClosed) {
|
||||
if (mFD) {
|
||||
(void) PR_Close(mFD);
|
||||
mFD = nullptr;
|
||||
}
|
||||
mClosed = true;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheInputStream::Available(uint64_t * bytesAvailable)
|
||||
{
|
||||
if (mClosed) return NS_BASE_STREAM_CLOSED;
|
||||
if (mStreamEnd < mPos) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
*bytesAvailable = mStreamEnd - mPos;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheInputStream::Read(char * buffer, uint32_t count, uint32_t * bytesRead)
|
||||
{
|
||||
*bytesRead = 0;
|
||||
|
||||
if (mClosed) {
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
|
||||
"[stream=%p] stream was closed",
|
||||
this, buffer, count));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mPos == mStreamEnd) {
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
|
||||
"[stream=%p] stream at end of file",
|
||||
this, buffer, count));
|
||||
return NS_OK;
|
||||
}
|
||||
if (mPos > mStreamEnd) {
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
|
||||
"[stream=%p] stream past end of file (!)",
|
||||
this, buffer, count));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (count > mStreamEnd - mPos)
|
||||
count = mStreamEnd - mPos;
|
||||
|
||||
if (mFD) {
|
||||
// just read from file
|
||||
int32_t result = PR_Read(mFD, buffer, count);
|
||||
if (result < 0) {
|
||||
nsresult rv = NS_ErrorAccordingToNSPR();
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read PR_Read failed"
|
||||
"[stream=%p, rv=%d, NSPR error %s",
|
||||
this, int(rv), PR_ErrorToName(PR_GetError())));
|
||||
return rv;
|
||||
}
|
||||
|
||||
mPos += (uint32_t)result;
|
||||
*bytesRead = (uint32_t)result;
|
||||
|
||||
} else if (mBuffer) {
|
||||
// read data from mBuffer
|
||||
memcpy(buffer, mBuffer + mPos, count);
|
||||
mPos += count;
|
||||
*bytesRead = count;
|
||||
} else {
|
||||
// no data source for input stream
|
||||
}
|
||||
|
||||
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheInputStream::Read "
|
||||
"[stream=%p, count=%ud, byteRead=%ud] ",
|
||||
this, unsigned(count), unsigned(*bytesRead)));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer,
|
||||
void * closure,
|
||||
uint32_t count,
|
||||
uint32_t * bytesRead)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheInputStream::IsNonBlocking(bool * nonBlocking)
|
||||
{
|
||||
*nonBlocking = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsDiskCacheStreamIO
|
||||
*****************************************************************************/
|
||||
NS_IMPL_ISUPPORTS(nsDiskCacheStreamIO, nsIOutputStream)
|
||||
|
||||
nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding)
|
||||
: mBinding(binding)
|
||||
, mInStreamCount(0)
|
||||
, mFD(nullptr)
|
||||
, mStreamEnd(0)
|
||||
, mBufSize(0)
|
||||
, mBuffer(nullptr)
|
||||
, mOutputStreamIsOpen(false)
|
||||
{
|
||||
mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice();
|
||||
|
||||
// acquire "death grip" on cache service
|
||||
nsCacheService *service = nsCacheService::GlobalInstance();
|
||||
NS_ADDREF(service);
|
||||
}
|
||||
|
||||
|
||||
nsDiskCacheStreamIO::~nsDiskCacheStreamIO()
|
||||
{
|
||||
nsCacheService::AssertOwnsLock();
|
||||
|
||||
// Close the outputstream
|
||||
if (mBinding && mOutputStreamIsOpen) {
|
||||
(void)CloseOutputStream();
|
||||
}
|
||||
|
||||
// release "death grip" on cache service
|
||||
nsCacheService *service = nsCacheService::GlobalInstance();
|
||||
NS_RELEASE(service);
|
||||
|
||||
// assert streams closed
|
||||
NS_ASSERTION(!mOutputStreamIsOpen, "output stream still open");
|
||||
NS_ASSERTION(mInStreamCount == 0, "input stream still open");
|
||||
NS_ASSERTION(!mFD, "file descriptor not closed");
|
||||
|
||||
DeleteBuffer();
|
||||
}
|
||||
|
||||
|
||||
// NOTE: called with service lock held
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::GetInputStream(uint32_t offset, nsIInputStream ** inputStream)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(inputStream);
|
||||
NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
*inputStream = nullptr;
|
||||
|
||||
if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
if (mOutputStreamIsOpen) {
|
||||
NS_WARNING("already have an output stream open");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
PRFileDesc * fd = nullptr;
|
||||
|
||||
mStreamEnd = mBinding->mCacheEntry->DataSize();
|
||||
if (mStreamEnd == 0) {
|
||||
// there's no data to read
|
||||
NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size");
|
||||
} else if (mBinding->mRecord.DataFile() == 0) {
|
||||
// open file desc for data
|
||||
rv = OpenCacheFile(PR_RDONLY, &fd);
|
||||
if (NS_FAILED(rv)) return rv; // unable to open file
|
||||
NS_ASSERTION(fd, "cache stream lacking open file.");
|
||||
|
||||
} else if (!mBuffer) {
|
||||
// read block file for data
|
||||
rv = ReadCacheBlocks(mStreamEnd);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
// else, mBuffer already contains all of the data (left over from a
|
||||
// previous block-file read or write).
|
||||
|
||||
NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream");
|
||||
|
||||
// create a new input stream
|
||||
nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd);
|
||||
if (!inStream) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
NS_ADDREF(*inputStream = inStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: called with service lock held
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::GetOutputStream(uint32_t offset, nsIOutputStream ** outputStream)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(outputStream);
|
||||
*outputStream = nullptr;
|
||||
|
||||
if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
NS_ASSERTION(!mOutputStreamIsOpen, "already have an output stream open");
|
||||
NS_ASSERTION(mInStreamCount == 0, "we already have input streams open");
|
||||
if (mOutputStreamIsOpen || mInStreamCount) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
mStreamEnd = mBinding->mCacheEntry->DataSize();
|
||||
|
||||
// Inits file or buffer and truncate at the desired offset
|
||||
nsresult rv = SeekAndTruncate(offset);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mOutputStreamIsOpen = true;
|
||||
NS_ADDREF(*outputStream = this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::ClearBinding()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (mBinding && mOutputStreamIsOpen)
|
||||
rv = CloseOutputStream();
|
||||
mBinding = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheStreamIO::Close()
|
||||
{
|
||||
if (!mOutputStreamIsOpen) return NS_OK;
|
||||
|
||||
// grab service lock
|
||||
nsCacheServiceAutoLock lock;
|
||||
|
||||
if (!mBinding) { // if we're severed, just clear member variables
|
||||
mOutputStreamIsOpen = false;
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv = CloseOutputStream();
|
||||
if (NS_FAILED(rv))
|
||||
NS_WARNING("CloseOutputStream() failed");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::CloseOutputStream()
|
||||
{
|
||||
NS_ASSERTION(mBinding, "oops");
|
||||
|
||||
CACHE_LOG_DEBUG(("CACHE: CloseOutputStream [%x doomed=%u]\n",
|
||||
mBinding->mRecord.HashNumber(), mBinding->mDoomed));
|
||||
|
||||
// Mark outputstream as closed, even if saving the stream fails
|
||||
mOutputStreamIsOpen = false;
|
||||
|
||||
// When writing to a file, just close the file
|
||||
if (mFD) {
|
||||
(void) PR_Close(mFD);
|
||||
mFD = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// write data to cache blocks, or flush mBuffer to file
|
||||
NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "stream is bigger than buffer");
|
||||
|
||||
nsDiskCacheMap *cacheMap = mDevice->CacheMap(); // get map reference
|
||||
nsDiskCacheRecord * record = &mBinding->mRecord;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// delete existing storage
|
||||
if (record->DataLocationInitialized()) {
|
||||
rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Only call UpdateRecord when there is no data to write,
|
||||
// because WriteDataCacheBlocks / FlushBufferToFile calls it.
|
||||
if ((mStreamEnd == 0) && (!mBinding->mDoomed)) {
|
||||
rv = cacheMap->UpdateRecord(record);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("cacheMap->UpdateRecord() failed.");
|
||||
return rv; // XXX doom cache entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mStreamEnd == 0) return NS_OK; // nothing to write
|
||||
|
||||
// try to write to the cache blocks
|
||||
rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("WriteDataCacheBlocks() failed.");
|
||||
|
||||
// failed to store in cacheblocks, save as separate file
|
||||
rv = FlushBufferToFile(); // initializes DataFileLocation() if necessary
|
||||
if (mFD) {
|
||||
UpdateFileSize();
|
||||
(void) PR_Close(mFD);
|
||||
mFD = nullptr;
|
||||
}
|
||||
else
|
||||
NS_WARNING("no file descriptor");
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// assumptions:
|
||||
// only one thread writing at a time
|
||||
// never have both output and input streams open
|
||||
// OnDataSizeChanged() will have already been called to update entry->DataSize()
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheStreamIO::Write( const char * buffer,
|
||||
uint32_t count,
|
||||
uint32_t * bytesWritten)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(buffer);
|
||||
NS_ENSURE_ARG_POINTER(bytesWritten);
|
||||
if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED;
|
||||
|
||||
*bytesWritten = 0; // always initialize to zero in case of errors
|
||||
|
||||
NS_ASSERTION(count, "Write called with count of zero");
|
||||
if (count == 0) {
|
||||
return NS_OK; // nothing to write
|
||||
}
|
||||
|
||||
// grab service lock
|
||||
nsCacheServiceAutoLock lock;
|
||||
if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
if (mInStreamCount) {
|
||||
// we have open input streams already
|
||||
// this is an error until we support overlapped I/O
|
||||
NS_WARNING("Attempting to write to cache entry with open input streams.\n");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Not writing to file, and it will fit in the cachedatablocks?
|
||||
if (!mFD && (mStreamEnd + count <= kMaxBufferSize)) {
|
||||
|
||||
// We have more data than the current buffer size?
|
||||
if ((mStreamEnd + count > mBufSize) && (mBufSize < kMaxBufferSize)) {
|
||||
// Increase buffer to the maximum size.
|
||||
mBuffer = (char *) moz_xrealloc(mBuffer, kMaxBufferSize);
|
||||
mBufSize = kMaxBufferSize;
|
||||
}
|
||||
|
||||
// Store in the buffer but only if it fits
|
||||
if (mStreamEnd + count <= mBufSize) {
|
||||
memcpy(mBuffer + mStreamEnd, buffer, count);
|
||||
mStreamEnd += count;
|
||||
*bytesWritten = count;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// There are more bytes than fit in the buffer/cacheblocks, switch to file
|
||||
if (!mFD) {
|
||||
// Opens a cache file and write the buffer to it
|
||||
nsresult rv = FlushBufferToFile();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
// Write directly to the file
|
||||
if (PR_Write(mFD, buffer, count) != (int32_t)count) {
|
||||
NS_WARNING("failed to write all data");
|
||||
return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
|
||||
}
|
||||
mStreamEnd += count;
|
||||
*bytesWritten = count;
|
||||
|
||||
UpdateFileSize();
|
||||
NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsDiskCacheStreamIO::UpdateFileSize()
|
||||
{
|
||||
NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called");
|
||||
|
||||
nsDiskCacheRecord * record = &mBinding->mRecord;
|
||||
const uint32_t oldSizeK = record->DataFileSize();
|
||||
uint32_t newSizeK = (mStreamEnd + 0x03FF) >> 10;
|
||||
|
||||
// make sure the size won't overflow (bug #651100)
|
||||
if (newSizeK > kMaxDataSizeK)
|
||||
newSizeK = kMaxDataSizeK;
|
||||
|
||||
if (newSizeK == oldSizeK) return;
|
||||
|
||||
record->SetDataFileSize(newSizeK);
|
||||
|
||||
// update cache size totals
|
||||
nsDiskCacheMap * cacheMap = mDevice->CacheMap();
|
||||
cacheMap->DecrementTotalSize(oldSizeK); // decrement old size
|
||||
cacheMap->IncrementTotalSize(newSizeK); // increment new size
|
||||
|
||||
if (!mBinding->mDoomed) {
|
||||
nsresult rv = cacheMap->UpdateRecord(record);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("cacheMap->UpdateRecord() failed.");
|
||||
// XXX doom cache entry?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::OpenCacheFile(int flags, PRFileDesc ** fd)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(fd);
|
||||
|
||||
CACHE_LOG_DEBUG(("nsDiskCacheStreamIO::OpenCacheFile"));
|
||||
|
||||
nsresult rv;
|
||||
nsDiskCacheMap * cacheMap = mDevice->CacheMap();
|
||||
nsCOMPtr<nsIFile> localFile;
|
||||
|
||||
rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord,
|
||||
nsDiskCache::kData,
|
||||
!!(flags & PR_CREATE_FILE),
|
||||
getter_AddRefs(localFile));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// create PRFileDesc for input stream - the 00600 is just for consistency
|
||||
return localFile->OpenNSPRFileDesc(flags, 00600, fd);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::ReadCacheBlocks(uint32_t bufferSize)
|
||||
{
|
||||
NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream");
|
||||
NS_ASSERTION(bufferSize <= kMaxBufferSize, "bufferSize too large for buffer");
|
||||
NS_ASSERTION(mStreamEnd <= bufferSize, "data too large for buffer");
|
||||
|
||||
nsDiskCacheRecord * record = &mBinding->mRecord;
|
||||
if (!record->DataLocationInitialized()) return NS_OK;
|
||||
|
||||
NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file");
|
||||
|
||||
if (!mBuffer) {
|
||||
mBuffer = (char *) moz_xmalloc(bufferSize);
|
||||
mBufSize = bufferSize;
|
||||
}
|
||||
|
||||
// read data stored in cache block files
|
||||
nsDiskCacheMap *map = mDevice->CacheMap(); // get map reference
|
||||
return map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::FlushBufferToFile()
|
||||
{
|
||||
nsresult rv;
|
||||
nsDiskCacheRecord * record = &mBinding->mRecord;
|
||||
|
||||
if (!mFD) {
|
||||
if (record->DataLocationInitialized() && (record->DataFile() > 0)) {
|
||||
// remove cache block storage
|
||||
nsDiskCacheMap * cacheMap = mDevice->CacheMap();
|
||||
rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
record->SetDataFileGeneration(mBinding->mGeneration);
|
||||
|
||||
// allocate file
|
||||
rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
int64_t dataSize = mBinding->mCacheEntry->PredictedDataSize();
|
||||
if (dataSize != -1)
|
||||
mozilla::fallocate(mFD, std::min<int64_t>(dataSize, kPreallocateLimit));
|
||||
}
|
||||
|
||||
// write buffer to the file when there is data in it
|
||||
if (mStreamEnd > 0) {
|
||||
if (!mBuffer) {
|
||||
NS_RUNTIMEABORT("Fix me!");
|
||||
}
|
||||
if (PR_Write(mFD, mBuffer, mStreamEnd) != (int32_t)mStreamEnd) {
|
||||
NS_WARNING("failed to flush all data");
|
||||
return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
|
||||
}
|
||||
}
|
||||
|
||||
// buffer is no longer valid
|
||||
DeleteBuffer();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsDiskCacheStreamIO::DeleteBuffer()
|
||||
{
|
||||
if (mBuffer) {
|
||||
free(mBuffer);
|
||||
mBuffer = nullptr;
|
||||
mBufSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
nsDiskCacheStreamIO::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
size_t usage = aMallocSizeOf(this);
|
||||
|
||||
usage += aMallocSizeOf(mFD);
|
||||
usage += aMallocSizeOf(mBuffer);
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDiskCacheStreamIO::SeekAndTruncate(uint32_t offset)
|
||||
{
|
||||
if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
if (uint32_t(offset) > mStreamEnd) return NS_ERROR_FAILURE;
|
||||
|
||||
// Set the current end to the desired offset
|
||||
mStreamEnd = offset;
|
||||
|
||||
// Currently stored in file?
|
||||
if (mBinding->mRecord.DataLocationInitialized() &&
|
||||
(mBinding->mRecord.DataFile() == 0)) {
|
||||
if (!mFD) {
|
||||
// we need an mFD, we better open it now
|
||||
nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
if (offset) {
|
||||
if (PR_Seek(mFD, offset, PR_SEEK_SET) == -1)
|
||||
return NS_ErrorAccordingToNSPR();
|
||||
}
|
||||
nsDiskCache::Truncate(mFD, offset);
|
||||
UpdateFileSize();
|
||||
|
||||
// When we starting at zero again, close file and start with buffer.
|
||||
// If offset is non-zero (and within buffer) an option would be
|
||||
// to read the file into the buffer, but chance is high that it is
|
||||
// rewritten to the file anyway.
|
||||
if (offset == 0) {
|
||||
// close file descriptor
|
||||
(void) PR_Close(mFD);
|
||||
mFD = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// read data into mBuffer if not read yet.
|
||||
if (offset && !mBuffer) {
|
||||
nsresult rv = ReadCacheBlocks(kMaxBufferSize);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
// stream buffer sanity check
|
||||
NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "bad stream");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheStreamIO::Flush()
|
||||
{
|
||||
if (!mOutputStreamIsOpen) return NS_BASE_STREAM_CLOSED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheStreamIO::WriteFrom(nsIInputStream *inStream, uint32_t count, uint32_t *bytesWritten)
|
||||
{
|
||||
NS_NOTREACHED("WriteFrom");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheStreamIO::WriteSegments( nsReadSegmentFun reader,
|
||||
void * closure,
|
||||
uint32_t count,
|
||||
uint32_t * bytesWritten)
|
||||
{
|
||||
NS_NOTREACHED("WriteSegments");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDiskCacheStreamIO::IsNonBlocking(bool * nonBlocking)
|
||||
{
|
||||
*nonBlocking = false;
|
||||
return NS_OK;
|
||||
}
|
||||
70
netwerk/cache/nsDiskCacheStreams.h
vendored
Normal file
70
netwerk/cache/nsDiskCacheStreams.h
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#ifndef _nsDiskCacheStreams_h_
|
||||
#define _nsDiskCacheStreams_h_
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsDiskCacheBinding.h"
|
||||
|
||||
#include "nsCache.h"
|
||||
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIOutputStream.h"
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
class nsDiskCacheDevice;
|
||||
|
||||
class nsDiskCacheStreamIO : public nsIOutputStream {
|
||||
public:
|
||||
explicit nsDiskCacheStreamIO(nsDiskCacheBinding * binding);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOUTPUTSTREAM
|
||||
|
||||
nsresult GetInputStream(uint32_t offset, nsIInputStream ** inputStream);
|
||||
nsresult GetOutputStream(uint32_t offset, nsIOutputStream ** outputStream);
|
||||
|
||||
nsresult ClearBinding();
|
||||
|
||||
void IncrementInputStreamCount() { mInStreamCount++; }
|
||||
void DecrementInputStreamCount()
|
||||
{
|
||||
mInStreamCount--;
|
||||
NS_ASSERTION(mInStreamCount >= 0, "mInStreamCount has gone negative");
|
||||
}
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
// GCC 2.95.2 requires this to be defined, although we never call it.
|
||||
// and OS/2 requires that it not be private
|
||||
nsDiskCacheStreamIO() { NS_NOTREACHED("oops"); }
|
||||
|
||||
private:
|
||||
virtual ~nsDiskCacheStreamIO();
|
||||
|
||||
nsresult OpenCacheFile(int flags, PRFileDesc ** fd);
|
||||
nsresult ReadCacheBlocks(uint32_t bufferSize);
|
||||
nsresult FlushBufferToFile();
|
||||
void UpdateFileSize();
|
||||
void DeleteBuffer();
|
||||
nsresult CloseOutputStream();
|
||||
nsresult SeekAndTruncate(uint32_t offset);
|
||||
|
||||
nsDiskCacheBinding * mBinding; // not an owning reference
|
||||
nsDiskCacheDevice * mDevice;
|
||||
mozilla::Atomic<int32_t> mInStreamCount;
|
||||
PRFileDesc * mFD;
|
||||
|
||||
uint32_t mStreamEnd; // current size of data
|
||||
uint32_t mBufSize; // current end of buffer
|
||||
char * mBuffer;
|
||||
bool mOutputStreamIsOpen;
|
||||
};
|
||||
|
||||
#endif // _nsDiskCacheStreams_h_
|
||||
142
netwerk/cache/nsICache.idl
vendored
Normal file
142
netwerk/cache/nsICache.idl
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
typedef long nsCacheStoragePolicy;
|
||||
typedef long nsCacheAccessMode;
|
||||
|
||||
/**
|
||||
* nsICache is a namespace for various cache constants. It does not represent
|
||||
* an actual object.
|
||||
*/
|
||||
[scriptable, uuid(d6c67f38-b39a-4582-8a48-4c4f8a56dfd0)]
|
||||
interface nsICache
|
||||
{
|
||||
/**
|
||||
* Access Modes
|
||||
*
|
||||
*
|
||||
* Mode Requested | Not Cached | Cached
|
||||
* ------------------------------------------------------------------------
|
||||
* READ | KEY_NOT_FOUND | NS_OK
|
||||
* | Mode = NONE | Mode = READ
|
||||
* | No Descriptor | Descriptor
|
||||
* ------------------------------------------------------------------------
|
||||
* WRITE | NS_OK | NS_OK (Cache service
|
||||
* | Mode = WRITE | Mode = WRITE dooms existing
|
||||
* | Descriptor | Descriptor cache entry)
|
||||
* ------------------------------------------------------------------------
|
||||
* READ_WRITE | NS_OK | NS_OK
|
||||
* (1st req.) | Mode = WRITE | Mode = READ_WRITE
|
||||
* | Descriptor | Descriptor
|
||||
* ------------------------------------------------------------------------
|
||||
* READ_WRITE | N/A | NS_OK
|
||||
* (Nth req.) | | Mode = READ
|
||||
* | | Descriptor
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
*
|
||||
* Access Requested:
|
||||
*
|
||||
* READ - I only want to READ, if there isn't an entry just fail
|
||||
* WRITE - I have something new I want to write into the cache, make
|
||||
* me a new entry and doom the old one, if any.
|
||||
* READ_WRITE - I want to READ, but I'm willing to update an existing
|
||||
* entry if necessary, or create a new one if none exists.
|
||||
*
|
||||
*
|
||||
* Access Granted:
|
||||
*
|
||||
* NONE - No descriptor is provided. You get zilch. Nada. Nothing.
|
||||
* READ - You can READ from this descriptor.
|
||||
* WRITE - You must WRITE to this descriptor because the cache entry
|
||||
* was just created for you.
|
||||
* READ_WRITE - You can READ the descriptor to determine if it's valid,
|
||||
* you may WRITE if it needs updating.
|
||||
*
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
* If you think that you might need to modify cached data or meta data,
|
||||
* then you must open a cache entry requesting WRITE access. Only one
|
||||
* cache entry descriptor, per cache entry, will be granted WRITE access.
|
||||
*
|
||||
* Usually, you will request READ_WRITE access in order to first test the
|
||||
* meta data and informational fields to determine if a write (ie. going
|
||||
* to the net) may actually be necessary. If you determine that it is
|
||||
* not, then you would mark the cache entry as valid (using MarkValid) and
|
||||
* then simply read the data from the cache.
|
||||
*
|
||||
* A descriptor granted WRITE access has exclusive access to the cache
|
||||
* entry up to the point at which it marks it as valid. Once the cache
|
||||
* entry has been "validated", other descriptors with READ access may be
|
||||
* opened to the cache entry.
|
||||
*
|
||||
* If you make a request for READ_WRITE access to a cache entry, the cache
|
||||
* service will downgrade your access to READ if there is already a
|
||||
* cache entry descriptor open with WRITE access.
|
||||
*
|
||||
* If you make a request for only WRITE access to a cache entry and another
|
||||
* descriptor with WRITE access is currently open, then the existing cache
|
||||
* entry will be 'doomed', and you will be given a descriptor (with WRITE
|
||||
* access only) to a new cache entry.
|
||||
*
|
||||
*/
|
||||
const nsCacheAccessMode ACCESS_NONE = 0;
|
||||
const nsCacheAccessMode ACCESS_READ = 1;
|
||||
const nsCacheAccessMode ACCESS_WRITE = 2;
|
||||
const nsCacheAccessMode ACCESS_READ_WRITE = 3;
|
||||
|
||||
/**
|
||||
* Storage Policy
|
||||
*
|
||||
* The storage policy of a cache entry determines the device(s) to which
|
||||
* it belongs. See nsICacheSession and nsICacheEntryDescriptor for more
|
||||
* details.
|
||||
*
|
||||
* STORE_ANYWHERE - Allows the cache entry to be stored in any device.
|
||||
* The cache service decides which cache device to use
|
||||
* based on "some resource management calculation."
|
||||
* STORE_IN_MEMORY - Requires the cache entry to reside in non-persistent
|
||||
* storage (ie. typically in system RAM).
|
||||
* STORE_ON_DISK - Requires the cache entry to reside in persistent
|
||||
* storage (ie. typically on a system's hard disk).
|
||||
* STORE_OFFLINE - Requires the cache entry to reside in persistent,
|
||||
* reliable storage for offline use.
|
||||
*/
|
||||
const nsCacheStoragePolicy STORE_ANYWHERE = 0;
|
||||
const nsCacheStoragePolicy STORE_IN_MEMORY = 1;
|
||||
const nsCacheStoragePolicy STORE_ON_DISK = 2;
|
||||
// value 3 was used by STORE_ON_DISK_AS_FILE which was removed
|
||||
const nsCacheStoragePolicy STORE_OFFLINE = 4;
|
||||
|
||||
/**
|
||||
* All entries for a cache session are stored as streams of data or
|
||||
* as objects. These constant my be used to specify the type of entries
|
||||
* when calling nsICacheService::CreateSession().
|
||||
*/
|
||||
const long NOT_STREAM_BASED = 0;
|
||||
const long STREAM_BASED = 1;
|
||||
|
||||
/**
|
||||
* The synchronous OpenCacheEntry() may be blocking or non-blocking. If a cache entry is
|
||||
* waiting to be validated by another cache descriptor (so no new cache descriptors for that
|
||||
* key can be created, OpenCacheEntry() will return NS_ERROR_CACHE_WAIT_FOR_VALIDATION in
|
||||
* non-blocking mode. In blocking mode, it will wait until the cache entry for the key has
|
||||
* been validated or doomed. If the cache entry is validated, then a descriptor for that
|
||||
* entry will be created and returned. If the cache entry was doomed, then a descriptor
|
||||
* will be created for a new cache entry for the key.
|
||||
*/
|
||||
const long NON_BLOCKING = 0;
|
||||
const long BLOCKING = 1;
|
||||
|
||||
/**
|
||||
* Constant meaning no expiration time.
|
||||
*/
|
||||
const unsigned long NO_EXPIRATION_TIME = 0xFFFFFFFF;
|
||||
};
|
||||
|
||||
164
netwerk/cache/nsICacheEntryDescriptor.idl
vendored
Normal file
164
netwerk/cache/nsICacheEntryDescriptor.idl
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsICacheVisitor.idl"
|
||||
#include "nsICache.idl"
|
||||
|
||||
interface nsISimpleEnumerator;
|
||||
interface nsICacheListener;
|
||||
interface nsIInputStream;
|
||||
interface nsIOutputStream;
|
||||
interface nsIFile;
|
||||
interface nsICacheMetaDataVisitor;
|
||||
|
||||
|
||||
[scriptable, uuid(90b17d31-46aa-4fb1-a206-473c966cbc18)]
|
||||
interface nsICacheEntryDescriptor : nsICacheEntryInfo
|
||||
{
|
||||
/**
|
||||
* Set the time at which the cache entry should be considered invalid (in
|
||||
* seconds since the Epoch).
|
||||
*/
|
||||
void setExpirationTime(in uint32_t expirationTime);
|
||||
|
||||
/**
|
||||
* Set the cache entry data size. This will fail if the cache entry
|
||||
* IS stream based.
|
||||
*/
|
||||
void setDataSize(in unsigned long size);
|
||||
|
||||
/**
|
||||
* Open blocking input stream to cache data. This will fail if the cache
|
||||
* entry IS NOT stream based. Use the stream transport service to
|
||||
* asynchronously read this stream on a background thread. The returned
|
||||
* stream MAY implement nsISeekableStream.
|
||||
*
|
||||
* @param offset
|
||||
* read starting from this offset into the cached data. an offset
|
||||
* beyond the end of the stream has undefined consequences.
|
||||
*
|
||||
* @return blocking, unbuffered input stream.
|
||||
*/
|
||||
nsIInputStream openInputStream(in unsigned long offset);
|
||||
|
||||
/**
|
||||
* Open blocking output stream to cache data. This will fail if the cache
|
||||
* entry IS NOT stream based. Use the stream transport service to
|
||||
* asynchronously write to this stream on a background thread. The returned
|
||||
* stream MAY implement nsISeekableStream.
|
||||
*
|
||||
* If opening an output stream to existing cached data, the data will be
|
||||
* truncated to the specified offset.
|
||||
*
|
||||
* @param offset
|
||||
* write starting from this offset into the cached data. an offset
|
||||
* beyond the end of the stream has undefined consequences.
|
||||
*
|
||||
* @return blocking, unbuffered output stream.
|
||||
*/
|
||||
nsIOutputStream openOutputStream(in unsigned long offset);
|
||||
|
||||
/**
|
||||
* Get/set the cache data element. This will fail if the cache entry
|
||||
* IS stream based. The cache entry holds a strong reference to this
|
||||
* object. The object will be released when the cache entry is destroyed.
|
||||
*/
|
||||
attribute nsISupports cacheElement;
|
||||
|
||||
/**
|
||||
* Stores the Content-Length specified in the HTTP header for this
|
||||
* entry. Checked before we write to the cache entry, to prevent ever
|
||||
* taking up space in the cache for an entry that we know up front
|
||||
* is going to have to be evicted anyway. See bug 588507.
|
||||
*/
|
||||
attribute int64_t predictedDataSize;
|
||||
|
||||
/**
|
||||
* Get the access granted to this descriptor. See nsICache.idl for the
|
||||
* definitions of the access modes and a thorough description of their
|
||||
* corresponding meanings.
|
||||
*/
|
||||
readonly attribute nsCacheAccessMode accessGranted;
|
||||
|
||||
/**
|
||||
* Get/set the storage policy of the cache entry. See nsICache.idl for
|
||||
* the definitions of the storage policies.
|
||||
*/
|
||||
attribute nsCacheStoragePolicy storagePolicy;
|
||||
|
||||
/**
|
||||
* Get the disk file associated with the cache entry.
|
||||
*/
|
||||
readonly attribute nsIFile file;
|
||||
|
||||
/**
|
||||
* Get/set security info on the cache entry for this descriptor. This fails
|
||||
* if the storage policy is not STORE_IN_MEMORY.
|
||||
*/
|
||||
attribute nsISupports securityInfo;
|
||||
|
||||
/**
|
||||
* Get the size of the cache entry data, as stored. This may differ
|
||||
* from the entry's dataSize, if the entry is compressed.
|
||||
*/
|
||||
readonly attribute unsigned long storageDataSize;
|
||||
|
||||
/**
|
||||
* Doom the cache entry this descriptor references in order to slate it for
|
||||
* removal. Once doomed a cache entry cannot be undoomed.
|
||||
*
|
||||
* A descriptor with WRITE access can doom the cache entry and choose to
|
||||
* fail pending requests. This means that pending requests will not get
|
||||
* a cache descriptor. This is meant as a tool for clients that wish to
|
||||
* instruct pending requests to skip the cache.
|
||||
*/
|
||||
void doom();
|
||||
void doomAndFailPendingRequests(in nsresult status);
|
||||
|
||||
/**
|
||||
* Asynchronously doom an entry. Listener will be notified about the status
|
||||
* of the operation. Null may be passed if caller doesn't care about the
|
||||
* result.
|
||||
*/
|
||||
void asyncDoom(in nsICacheListener listener);
|
||||
|
||||
/**
|
||||
* A writer must validate this cache object before any readers are given
|
||||
* a descriptor to the object.
|
||||
*/
|
||||
void markValid();
|
||||
|
||||
/**
|
||||
* Explicitly close the descriptor (optional).
|
||||
*/
|
||||
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Methods for accessing meta data. Meta data is a table of key/value
|
||||
* string pairs. The strings do not have to conform to any particular
|
||||
* charset, but they must be null terminated.
|
||||
*/
|
||||
string getMetaDataElement(in string key);
|
||||
void setMetaDataElement(in string key, in string value);
|
||||
|
||||
/**
|
||||
* Visitor will be called with key/value pair for each meta data element.
|
||||
*/
|
||||
void visitMetaData(in nsICacheMetaDataVisitor visitor);
|
||||
};
|
||||
|
||||
|
||||
|
||||
[scriptable, uuid(22f9a49c-3cf8-4c23-8006-54efb11ac562)]
|
||||
interface nsICacheMetaDataVisitor : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called for each key/value pair in the meta data for a cache entry
|
||||
*/
|
||||
boolean visitMetaDataElement(in string key,
|
||||
in string value);
|
||||
};
|
||||
32
netwerk/cache/nsICacheListener.idl
vendored
Normal file
32
netwerk/cache/nsICacheListener.idl
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsICache.idl"
|
||||
|
||||
|
||||
interface nsICacheEntryDescriptor;
|
||||
|
||||
[scriptable, uuid(8eadf2ed-8cac-4961-8025-6da6d5827e74)]
|
||||
interface nsICacheListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when the requested access (or appropriate subset) is
|
||||
* acquired. The status parameter equals NS_OK on success.
|
||||
* See nsICacheService.idl for accessGranted values.
|
||||
*/
|
||||
void onCacheEntryAvailable(in nsICacheEntryDescriptor descriptor,
|
||||
in nsCacheAccessMode accessGranted,
|
||||
in nsresult status);
|
||||
|
||||
/**
|
||||
* Called when nsCacheSession::DoomEntry() is completed. The status
|
||||
* parameter is NS_OK when the entry was doomed, or NS_ERROR_NOT_AVAILABLE
|
||||
* when there is no such entry.
|
||||
*/
|
||||
void onCacheEntryDoomed(in nsresult status);
|
||||
};
|
||||
98
netwerk/cache/nsICacheService.idl
vendored
Normal file
98
netwerk/cache/nsICacheService.idl
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsICache.idl"
|
||||
|
||||
interface nsISimpleEnumerator;
|
||||
interface nsICacheListener;
|
||||
interface nsICacheSession;
|
||||
interface nsICacheVisitor;
|
||||
interface nsIEventTarget;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* IMPORTANT NOTE: THIS INTERFACE IS NO LONGER SUPPORTED AND PLANNED TO BE
|
||||
* REMOVED SOON. WE STRONGLY ENCORAGE TO MIGRATE THE EXISTING CODE AND FOR
|
||||
* THE NEW CODE USE ONLY THE NEW HTTP CACHE API IN netwerk/cache2/.
|
||||
*/
|
||||
[scriptable, uuid(14dbe1e9-f3bc-45af-92f4-2c574fcd4e39)]
|
||||
interface nsICacheService : nsISupports
|
||||
{
|
||||
/**
|
||||
* @throws NS_ERROR_NOT_IMPLEMENTED when the cache v2 is prefered to use.
|
||||
*
|
||||
* Create a cache session
|
||||
*
|
||||
* A cache session represents a client's access into the cache. The cache
|
||||
* session is not "owned" by the cache service. Hence, it is possible to
|
||||
* create duplicate cache sessions. Entries created by a cache session
|
||||
* are invisible to other cache sessions, unless the cache sessions are
|
||||
* equivalent.
|
||||
*
|
||||
* @param clientID - Specifies the name of the client using the cache.
|
||||
* @param storagePolicy - Limits the storage policy for all entries
|
||||
* accessed via the returned session. As a result, devices excluded
|
||||
* by the storage policy will not be searched when opening entries
|
||||
* from the returned session.
|
||||
* @param streamBased - Indicates whether or not the data being cached
|
||||
* can be represented as a stream. The storagePolicy must be
|
||||
* consistent with the value of this field. For example, a non-stream-
|
||||
* based cache entry can only have a storage policy of STORE_IN_MEMORY.
|
||||
* @return new cache session.
|
||||
*/
|
||||
nsICacheSession createSession(in string clientID,
|
||||
in nsCacheStoragePolicy storagePolicy,
|
||||
in boolean streamBased);
|
||||
|
||||
/**
|
||||
* @throws NS_ERROR_NOT_IMPLEMENTED when the cache v2 is prefered to use.
|
||||
*
|
||||
* Visit entries stored in the cache. Used to implement about:cache.
|
||||
*/
|
||||
void visitEntries(in nsICacheVisitor visitor);
|
||||
|
||||
/**
|
||||
* @throws NS_ERROR_NOT_IMPLEMENTED when the cache v2 is prefered to use.
|
||||
*
|
||||
* Evicts all entries in all devices implied by the storage policy.
|
||||
*
|
||||
* @note This function may evict some items but will throw if it fails to evict
|
||||
* everything.
|
||||
*/
|
||||
void evictEntries(in nsCacheStoragePolicy storagePolicy);
|
||||
|
||||
/**
|
||||
* Event target which is used for I/O operations
|
||||
*/
|
||||
readonly attribute nsIEventTarget cacheIOTarget;
|
||||
};
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* Observer service notification that is sent when
|
||||
* nsICacheService::evictEntries() or nsICacheSession::evictEntries()
|
||||
* is called.
|
||||
*/
|
||||
#define NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID "cacheservice:empty-cache"
|
||||
%}
|
||||
|
||||
[scriptable, builtinclass, uuid(d0fc8d38-db80-4928-bf1c-b0085ddfa9dc)]
|
||||
interface nsICacheServiceInternal : nsICacheService
|
||||
{
|
||||
/**
|
||||
* This is an internal interface. It changes so frequently that it probably
|
||||
* went away while you were reading this.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Milliseconds for which the service lock has been held. 0 if unlocked.
|
||||
*/
|
||||
readonly attribute double lockHeldTime;
|
||||
};
|
||||
|
||||
|
||||
88
netwerk/cache/nsICacheSession.idl
vendored
Normal file
88
netwerk/cache/nsICacheSession.idl
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsICache.idl"
|
||||
|
||||
interface nsICacheEntryDescriptor;
|
||||
interface nsICacheListener;
|
||||
interface nsIFile;
|
||||
|
||||
[scriptable, uuid(1dd7708c-de48-4ffe-b5aa-cd218c762887)]
|
||||
interface nsICacheSession : nsISupports
|
||||
{
|
||||
/**
|
||||
* Expired entries will be doomed or evicted if this attribute is set to
|
||||
* true. If false, expired entries will be returned (useful for offline-
|
||||
* mode and clients, such as HTTP, that can update the valid lifetime of
|
||||
* cached content). This attribute defaults to true.
|
||||
*/
|
||||
attribute boolean doomEntriesIfExpired;
|
||||
|
||||
/**
|
||||
* When set, entries created with this session will be placed to a cache
|
||||
* based at this directory. Use when storing entries to a different
|
||||
* profile than the active profile of the the current running application
|
||||
* process.
|
||||
*/
|
||||
attribute nsIFile profileDirectory;
|
||||
|
||||
/**
|
||||
* A cache session can only give out one descriptor with WRITE access
|
||||
* to a given cache entry at a time. Until the client calls MarkValid on
|
||||
* its descriptor, other attempts to open the same cache entry will block.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Synchronous cache access. This method fails if it is called on the main
|
||||
* thread. Use asyncOpenCacheEntry() instead. This returns a unique
|
||||
* descriptor each time it is called, even if the same key is specified.
|
||||
* When called by multiple threads for write access, only one writable
|
||||
* descriptor will be granted. If 'blockingMode' is set to false, it will
|
||||
* return NS_ERROR_CACHE_WAIT_FOR_VALIDATION rather than block when another
|
||||
* descriptor has been given WRITE access but hasn't validated the entry yet.
|
||||
*/
|
||||
nsICacheEntryDescriptor openCacheEntry(in ACString key,
|
||||
in nsCacheAccessMode accessRequested,
|
||||
in boolean blockingMode);
|
||||
|
||||
/**
|
||||
* Asynchronous cache access. Does not block the calling thread. Instead,
|
||||
* the listener will be notified when the descriptor is available. If
|
||||
* 'noWait' is set to true, the listener will be notified immediately with
|
||||
* status NS_ERROR_CACHE_WAIT_FOR_VALIDATION rather than queuing the request
|
||||
* when another descriptor has been given WRITE access but hasn't validated
|
||||
* the entry yet.
|
||||
*/
|
||||
void asyncOpenCacheEntry(in ACString key,
|
||||
in nsCacheAccessMode accessRequested,
|
||||
in nsICacheListener listener,
|
||||
[optional] in boolean noWait);
|
||||
|
||||
/**
|
||||
* Evict all entries for this session's clientID according to its storagePolicy.
|
||||
*/
|
||||
void evictEntries();
|
||||
|
||||
/**
|
||||
* Return whether any of the cache devices implied by the session storage policy
|
||||
* are currently enabled for instantiation if they don't already exist.
|
||||
*/
|
||||
boolean isStorageEnabled();
|
||||
|
||||
/**
|
||||
* Asynchronously doom an entry specified by the key. Listener will be
|
||||
* notified about the status of the operation. Null may be passed if caller
|
||||
* doesn't care about the result.
|
||||
*/
|
||||
void doomEntry(in ACString key, in nsICacheListener listener);
|
||||
|
||||
/**
|
||||
* Private entries will be doomed when the last private browsing session
|
||||
* finishes.
|
||||
*/
|
||||
attribute boolean isPrivate;
|
||||
};
|
||||
123
netwerk/cache/nsICacheVisitor.idl
vendored
Normal file
123
netwerk/cache/nsICacheVisitor.idl
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/* XXX we should define device and entry info as well (stats, etc) */
|
||||
|
||||
interface nsICacheDeviceInfo;
|
||||
interface nsICacheEntryInfo;
|
||||
|
||||
|
||||
[scriptable, uuid(f8c08c4b-d778-49d1-a59b-866fdc500d95)]
|
||||
interface nsICacheVisitor : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called to provide information about a cache device.
|
||||
*
|
||||
* @param deviceID - specifies the device being visited.
|
||||
* @param deviceInfo - specifies information about this device.
|
||||
*
|
||||
* @return true to start visiting all entries for this device.
|
||||
* @return false to advance to the next device.
|
||||
*/
|
||||
boolean visitDevice(in string deviceID,
|
||||
in nsICacheDeviceInfo deviceInfo);
|
||||
|
||||
/**
|
||||
* Called to provide information about a cache entry.
|
||||
*
|
||||
* @param deviceID - specifies the device being visited.
|
||||
* @param entryInfo - specifies information about this entry.
|
||||
*
|
||||
* @return true to visit the next entry on the current device, or if the
|
||||
* end of the device has been reached, advance to the next device.
|
||||
* @return false to advance to the next device.
|
||||
*/
|
||||
boolean visitEntry(in string deviceID,
|
||||
in nsICacheEntryInfo entryInfo);
|
||||
};
|
||||
|
||||
|
||||
[scriptable, uuid(31d1c294-1dd2-11b2-be3a-c79230dca297)]
|
||||
interface nsICacheDeviceInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get a human readable description of the cache device.
|
||||
*/
|
||||
readonly attribute string description;
|
||||
|
||||
/**
|
||||
* Get a usage report, statistics, miscellaneous data about
|
||||
* the cache device.
|
||||
*/
|
||||
readonly attribute string usageReport;
|
||||
|
||||
/**
|
||||
* Get the number of stored cache entries.
|
||||
*/
|
||||
readonly attribute unsigned long entryCount;
|
||||
|
||||
/**
|
||||
* Get the total size of the stored cache entries.
|
||||
*/
|
||||
readonly attribute unsigned long totalSize;
|
||||
|
||||
/**
|
||||
* Get the upper limit of the size of the data the cache can store.
|
||||
*/
|
||||
readonly attribute unsigned long maximumSize;
|
||||
};
|
||||
|
||||
|
||||
[scriptable, uuid(fab51c92-95c3-4468-b317-7de4d7588254)]
|
||||
interface nsICacheEntryInfo : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get the client id associated with this cache entry.
|
||||
*/
|
||||
readonly attribute string clientID;
|
||||
|
||||
/**
|
||||
* Get the id for the device that stores this cache entry.
|
||||
*/
|
||||
readonly attribute string deviceID;
|
||||
|
||||
/**
|
||||
* Get the key identifying the cache entry.
|
||||
*/
|
||||
readonly attribute ACString key;
|
||||
|
||||
/**
|
||||
* Get the number of times the cache entry has been opened.
|
||||
*/
|
||||
readonly attribute long fetchCount;
|
||||
|
||||
/**
|
||||
* Get the last time the cache entry was opened (in seconds since the Epoch).
|
||||
*/
|
||||
readonly attribute uint32_t lastFetched;
|
||||
|
||||
/**
|
||||
* Get the last time the cache entry was modified (in seconds since the Epoch).
|
||||
*/
|
||||
readonly attribute uint32_t lastModified;
|
||||
|
||||
/**
|
||||
* Get the expiration time of the cache entry (in seconds since the Epoch).
|
||||
*/
|
||||
readonly attribute uint32_t expirationTime;
|
||||
|
||||
/**
|
||||
* Get the cache entry data size.
|
||||
*/
|
||||
readonly attribute unsigned long dataSize;
|
||||
|
||||
/**
|
||||
* Find out whether or not the cache entry is stream based.
|
||||
*/
|
||||
boolean isStreamBased();
|
||||
};
|
||||
617
netwerk/cache/nsMemoryCacheDevice.cpp
vendored
Normal file
617
netwerk/cache/nsMemoryCacheDevice.cpp
vendored
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCache.h"
|
||||
#include "nsMemoryCacheDevice.h"
|
||||
#include "nsCacheService.h"
|
||||
#include "nsICacheService.h"
|
||||
#include "nsICacheVisitor.h"
|
||||
#include "nsIStorageStream.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include <algorithm>
|
||||
|
||||
// The memory cache implements the "LRU-SP" caching algorithm
|
||||
// described in "LRU-SP: A Size-Adjusted and Popularity-Aware LRU Replacement
|
||||
// Algorithm for Web Caching" by Kai Cheng and Yahiko Kambayashi.
|
||||
|
||||
// We keep kQueueCount LRU queues, which should be about ceil(log2(mHardLimit))
|
||||
// The queues hold exponentially increasing ranges of floor(log2((size/nref)))
|
||||
// values for entries.
|
||||
// Entries larger than 2^(kQueueCount-1) go in the last queue.
|
||||
// Entries with no expiration go in the first queue.
|
||||
|
||||
const char *gMemoryDeviceID = "memory";
|
||||
using namespace mozilla;
|
||||
|
||||
nsMemoryCacheDevice::nsMemoryCacheDevice()
|
||||
: mInitialized(false),
|
||||
mHardLimit(4 * 1024 * 1024), // default, if no pref
|
||||
mSoftLimit((mHardLimit * 9) / 10), // default, if no pref
|
||||
mTotalSize(0),
|
||||
mInactiveSize(0),
|
||||
mEntryCount(0),
|
||||
mMaxEntryCount(0),
|
||||
mMaxEntrySize(-1) // -1 means "no limit"
|
||||
{
|
||||
for (int i=0; i<kQueueCount; ++i)
|
||||
PR_INIT_CLIST(&mEvictionList[i]);
|
||||
}
|
||||
|
||||
|
||||
nsMemoryCacheDevice::~nsMemoryCacheDevice()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::Init()
|
||||
{
|
||||
if (mInitialized) return NS_ERROR_ALREADY_INITIALIZED;
|
||||
|
||||
mMemCacheEntries.Init();
|
||||
mInitialized = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::Shutdown()
|
||||
{
|
||||
NS_ASSERTION(mInitialized, "### attempting shutdown while not initialized");
|
||||
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
mMemCacheEntries.Shutdown();
|
||||
|
||||
// evict all entries
|
||||
nsCacheEntry * entry, * next;
|
||||
|
||||
for (int i = kQueueCount - 1; i >= 0; --i) {
|
||||
entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
|
||||
while (entry != &mEvictionList[i]) {
|
||||
NS_ASSERTION(!entry->IsInUse(), "### shutting down with active entries");
|
||||
next = (nsCacheEntry *)PR_NEXT_LINK(entry);
|
||||
PR_REMOVE_AND_INIT_LINK(entry);
|
||||
|
||||
// update statistics
|
||||
int32_t memoryRecovered = (int32_t)entry->DataSize();
|
||||
mTotalSize -= memoryRecovered;
|
||||
mInactiveSize -= memoryRecovered;
|
||||
--mEntryCount;
|
||||
|
||||
delete entry;
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* we're not factoring in changes to meta data yet...
|
||||
* NS_ASSERTION(mTotalSize == 0, "### mem cache leaking entries?");
|
||||
*/
|
||||
NS_ASSERTION(mInactiveSize == 0, "### mem cache leaking entries?");
|
||||
NS_ASSERTION(mEntryCount == 0, "### mem cache leaking entries?");
|
||||
|
||||
mInitialized = false;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
nsMemoryCacheDevice::GetDeviceID()
|
||||
{
|
||||
return gMemoryDeviceID;
|
||||
}
|
||||
|
||||
|
||||
nsCacheEntry *
|
||||
nsMemoryCacheDevice::FindEntry(nsCString * key, bool *collision)
|
||||
{
|
||||
mozilla::Telemetry::AutoTimer<mozilla::Telemetry::CACHE_MEMORY_SEARCH_2> timer;
|
||||
nsCacheEntry * entry = mMemCacheEntries.GetEntry(key);
|
||||
if (!entry) return nullptr;
|
||||
|
||||
// move entry to the tail of an eviction list
|
||||
PR_REMOVE_AND_INIT_LINK(entry);
|
||||
PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]);
|
||||
|
||||
mInactiveSize -= entry->DataSize();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::DeactivateEntry(nsCacheEntry * entry)
|
||||
{
|
||||
CACHE_LOG_DEBUG(("nsMemoryCacheDevice::DeactivateEntry for entry 0x%p\n",
|
||||
entry));
|
||||
if (entry->IsDoomed()) {
|
||||
#ifdef DEBUG
|
||||
// XXX verify we've removed it from mMemCacheEntries & eviction list
|
||||
#endif
|
||||
delete entry;
|
||||
CACHE_LOG_DEBUG(("deleted doomed entry 0x%p\n", entry));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCacheEntry * ourEntry = mMemCacheEntries.GetEntry(entry->Key());
|
||||
NS_ASSERTION(ourEntry, "DeactivateEntry called for an entry we don't have!");
|
||||
NS_ASSERTION(entry == ourEntry, "entry doesn't match ourEntry");
|
||||
if (ourEntry != entry)
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
#endif
|
||||
|
||||
mInactiveSize += entry->DataSize();
|
||||
EvictEntriesIfNecessary();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::BindEntry(nsCacheEntry * entry)
|
||||
{
|
||||
if (!entry->IsDoomed()) {
|
||||
NS_ASSERTION(PR_CLIST_IS_EMPTY(entry),"entry is already on a list!");
|
||||
|
||||
// append entry to the eviction list
|
||||
PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]);
|
||||
|
||||
// add entry to hashtable of mem cache entries
|
||||
nsresult rv = mMemCacheEntries.AddEntry(entry);
|
||||
if (NS_FAILED(rv)) {
|
||||
PR_REMOVE_AND_INIT_LINK(entry);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// add size of entry to memory totals
|
||||
++mEntryCount;
|
||||
if (mMaxEntryCount < mEntryCount) mMaxEntryCount = mEntryCount;
|
||||
|
||||
mTotalSize += entry->DataSize();
|
||||
EvictEntriesIfNecessary();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsMemoryCacheDevice::DoomEntry(nsCacheEntry * entry)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// debug code to verify we have entry
|
||||
nsCacheEntry * hashEntry = mMemCacheEntries.GetEntry(entry->Key());
|
||||
if (!hashEntry) NS_WARNING("no entry for key");
|
||||
else if (entry != hashEntry) NS_WARNING("entry != hashEntry");
|
||||
#endif
|
||||
CACHE_LOG_DEBUG(("Dooming entry 0x%p in memory cache\n", entry));
|
||||
EvictEntry(entry, DO_NOT_DELETE_ENTRY);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::OpenInputStreamForEntry( nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIInputStream ** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(entry);
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
|
||||
nsCOMPtr<nsIStorageStream> storage;
|
||||
nsresult rv;
|
||||
|
||||
nsISupports *data = entry->Data();
|
||||
if (data) {
|
||||
storage = do_QueryInterface(data, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
}
|
||||
else {
|
||||
rv = NS_NewStorageStream(4096, uint32_t(-1), getter_AddRefs(storage));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
entry->SetData(storage);
|
||||
}
|
||||
|
||||
return storage->NewInputStream(offset, result);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::OpenOutputStreamForEntry( nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIOutputStream ** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(entry);
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
|
||||
nsCOMPtr<nsIStorageStream> storage;
|
||||
nsresult rv;
|
||||
|
||||
nsISupports *data = entry->Data();
|
||||
if (data) {
|
||||
storage = do_QueryInterface(data, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
}
|
||||
else {
|
||||
rv = NS_NewStorageStream(4096, uint32_t(-1), getter_AddRefs(storage));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
entry->SetData(storage);
|
||||
}
|
||||
|
||||
return storage->GetOutputStream(offset, result);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::GetFileForEntry( nsCacheEntry * entry,
|
||||
nsIFile ** result )
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
bool
|
||||
nsMemoryCacheDevice::EntryIsTooBig(int64_t entrySize)
|
||||
{
|
||||
CACHE_LOG_DEBUG(("nsMemoryCacheDevice::EntryIsTooBig "
|
||||
"[size=%d max=%d soft=%d]\n",
|
||||
entrySize, mMaxEntrySize, mSoftLimit));
|
||||
if (mMaxEntrySize == -1)
|
||||
return entrySize > mSoftLimit;
|
||||
else
|
||||
return (entrySize > mSoftLimit || entrySize > mMaxEntrySize);
|
||||
}
|
||||
|
||||
size_t
|
||||
nsMemoryCacheDevice::TotalSize()
|
||||
{
|
||||
return mTotalSize;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::OnDataSizeChange( nsCacheEntry * entry, int32_t deltaSize)
|
||||
{
|
||||
if (entry->IsStreamData()) {
|
||||
// we have the right to refuse or pre-evict
|
||||
uint32_t newSize = entry->DataSize() + deltaSize;
|
||||
if (EntryIsTooBig(newSize)) {
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
nsCacheService::DoomEntry(entry);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust our totals
|
||||
mTotalSize += deltaSize;
|
||||
|
||||
if (!entry->IsDoomed()) {
|
||||
// move entry to the tail of the appropriate eviction list
|
||||
PR_REMOVE_AND_INIT_LINK(entry);
|
||||
PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, deltaSize)]);
|
||||
}
|
||||
|
||||
EvictEntriesIfNecessary();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsMemoryCacheDevice::AdjustMemoryLimits(int32_t softLimit, int32_t hardLimit)
|
||||
{
|
||||
mSoftLimit = softLimit;
|
||||
mHardLimit = hardLimit;
|
||||
|
||||
// First, evict entries that won't fit into the new cache size.
|
||||
EvictEntriesIfNecessary();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsMemoryCacheDevice::EvictEntry(nsCacheEntry * entry, bool deleteEntry)
|
||||
{
|
||||
CACHE_LOG_DEBUG(("Evicting entry 0x%p from memory cache, deleting: %d\n",
|
||||
entry, deleteEntry));
|
||||
// remove entry from our hashtable
|
||||
mMemCacheEntries.RemoveEntry(entry);
|
||||
|
||||
// remove entry from the eviction list
|
||||
PR_REMOVE_AND_INIT_LINK(entry);
|
||||
|
||||
// update statistics
|
||||
int32_t memoryRecovered = (int32_t)entry->DataSize();
|
||||
mTotalSize -= memoryRecovered;
|
||||
if (!entry->IsDoomed())
|
||||
mInactiveSize -= memoryRecovered;
|
||||
--mEntryCount;
|
||||
|
||||
if (deleteEntry) delete entry;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsMemoryCacheDevice::EvictEntriesIfNecessary(void)
|
||||
{
|
||||
nsCacheEntry * entry;
|
||||
nsCacheEntry * maxEntry;
|
||||
CACHE_LOG_DEBUG(("EvictEntriesIfNecessary. mTotalSize: %d, mHardLimit: %d,"
|
||||
"mInactiveSize: %d, mSoftLimit: %d\n",
|
||||
mTotalSize, mHardLimit, mInactiveSize, mSoftLimit));
|
||||
|
||||
if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit))
|
||||
return;
|
||||
|
||||
uint32_t now = SecondsFromPRTime(PR_Now());
|
||||
uint64_t entryCost = 0;
|
||||
uint64_t maxCost = 0;
|
||||
do {
|
||||
// LRU-SP eviction selection: Check the head of each segment (each
|
||||
// eviction list, kept in LRU order) and select the maximal-cost
|
||||
// entry for eviction. Cost is time-since-accessed * size / nref.
|
||||
maxEntry = 0;
|
||||
for (int i = kQueueCount - 1; i >= 0; --i) {
|
||||
entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
|
||||
|
||||
// If the head of a list is in use, check the next available entry
|
||||
while ((entry != &mEvictionList[i]) &&
|
||||
(entry->IsInUse())) {
|
||||
entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
|
||||
}
|
||||
|
||||
if (entry != &mEvictionList[i]) {
|
||||
entryCost = (uint64_t)
|
||||
(now - entry->LastFetched()) * entry->DataSize() /
|
||||
std::max(1, entry->FetchCount());
|
||||
if (!maxEntry || (entryCost > maxCost)) {
|
||||
maxEntry = entry;
|
||||
maxCost = entryCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxEntry) {
|
||||
EvictEntry(maxEntry, DELETE_ENTRY);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ((mTotalSize >= mHardLimit) || (mInactiveSize >= mSoftLimit));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
nsMemoryCacheDevice::EvictionList(nsCacheEntry * entry, int32_t deltaSize)
|
||||
{
|
||||
// favor items which never expire by putting them in the lowest-index queue
|
||||
if (entry->ExpirationTime() == nsICache::NO_EXPIRATION_TIME)
|
||||
return 0;
|
||||
|
||||
// compute which eviction queue this entry should go into,
|
||||
// based on floor(log2(size/nref))
|
||||
int32_t size = deltaSize + (int32_t)entry->DataSize();
|
||||
int32_t fetchCount = std::max(1, entry->FetchCount());
|
||||
|
||||
return std::min((int)mozilla::FloorLog2(size / fetchCount), kQueueCount - 1);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::Visit(nsICacheVisitor * visitor)
|
||||
{
|
||||
nsMemoryCacheDeviceInfo * deviceInfo = new nsMemoryCacheDeviceInfo(this);
|
||||
nsCOMPtr<nsICacheDeviceInfo> deviceRef(deviceInfo);
|
||||
if (!deviceInfo) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
bool keepGoing;
|
||||
nsresult rv = visitor->VisitDevice(gMemoryDeviceID, deviceInfo, &keepGoing);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (!keepGoing)
|
||||
return NS_OK;
|
||||
|
||||
nsCacheEntry * entry;
|
||||
nsCOMPtr<nsICacheEntryInfo> entryRef;
|
||||
|
||||
for (int i = kQueueCount - 1; i >= 0; --i) {
|
||||
entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
|
||||
while (entry != &mEvictionList[i]) {
|
||||
nsCacheEntryInfo * entryInfo = new nsCacheEntryInfo(entry);
|
||||
if (!entryInfo) return NS_ERROR_OUT_OF_MEMORY;
|
||||
entryRef = entryInfo;
|
||||
|
||||
rv = visitor->VisitEntry(gMemoryDeviceID, entryInfo, &keepGoing);
|
||||
entryInfo->DetachEntry();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (!keepGoing) break;
|
||||
|
||||
entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
IsEntryPrivate(nsCacheEntry* entry, void* args)
|
||||
{
|
||||
return entry->IsPrivate();
|
||||
}
|
||||
|
||||
struct ClientIDArgs {
|
||||
const char* clientID;
|
||||
uint32_t prefixLength;
|
||||
};
|
||||
|
||||
static bool
|
||||
EntryMatchesClientID(nsCacheEntry* entry, void* args)
|
||||
{
|
||||
const char * clientID = static_cast<ClientIDArgs*>(args)->clientID;
|
||||
uint32_t prefixLength = static_cast<ClientIDArgs*>(args)->prefixLength;
|
||||
const char * key = entry->Key()->get();
|
||||
return !clientID || nsCRT::strncmp(clientID, key, prefixLength) == 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::DoEvictEntries(bool (*matchFn)(nsCacheEntry* entry, void* args), void* args)
|
||||
{
|
||||
nsCacheEntry * entry;
|
||||
|
||||
for (int i = kQueueCount - 1; i >= 0; --i) {
|
||||
PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]);
|
||||
while (elem != &mEvictionList[i]) {
|
||||
entry = (nsCacheEntry *)elem;
|
||||
elem = PR_NEXT_LINK(elem);
|
||||
|
||||
if (!matchFn(entry, args))
|
||||
continue;
|
||||
|
||||
if (entry->IsInUse()) {
|
||||
nsresult rv = nsCacheService::DoomEntry(entry);
|
||||
if (NS_FAILED(rv)) {
|
||||
CACHE_LOG_WARNING(("memCache->DoEvictEntries() aborted: rv =%x", rv));
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
EvictEntry(entry, DELETE_ENTRY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::EvictEntries(const char * clientID)
|
||||
{
|
||||
ClientIDArgs args = {clientID, clientID ? uint32_t(strlen(clientID)) : 0};
|
||||
return DoEvictEntries(&EntryMatchesClientID, &args);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMemoryCacheDevice::EvictPrivateEntries()
|
||||
{
|
||||
return DoEvictEntries(&IsEntryPrivate, nullptr);
|
||||
}
|
||||
|
||||
|
||||
// WARNING: SetCapacity can get called before Init()
|
||||
void
|
||||
nsMemoryCacheDevice::SetCapacity(int32_t capacity)
|
||||
{
|
||||
int32_t hardLimit = capacity * 1024; // convert k into bytes
|
||||
int32_t softLimit = (hardLimit * 9) / 10;
|
||||
AdjustMemoryLimits(softLimit, hardLimit);
|
||||
}
|
||||
|
||||
void
|
||||
nsMemoryCacheDevice::SetMaxEntrySize(int32_t maxSizeInKilobytes)
|
||||
{
|
||||
// Internal unit is bytes. Changing this only takes effect *after* the
|
||||
// change and has no consequences for existing cache-entries
|
||||
if (maxSizeInKilobytes >= 0)
|
||||
mMaxEntrySize = maxSizeInKilobytes * 1024;
|
||||
else
|
||||
mMaxEntrySize = -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
nsMemoryCacheDevice::CheckEntryCount()
|
||||
{
|
||||
if (!mInitialized) return;
|
||||
|
||||
int32_t evictionListCount = 0;
|
||||
for (int i=0; i<kQueueCount; ++i) {
|
||||
PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]);
|
||||
while (elem != &mEvictionList[i]) {
|
||||
elem = PR_NEXT_LINK(elem);
|
||||
++evictionListCount;
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(mEntryCount == evictionListCount, "### mem cache badness");
|
||||
|
||||
int32_t entryCount = 0;
|
||||
for (auto iter = mMemCacheEntries.Iter(); !iter.Done(); iter.Next()) {
|
||||
++entryCount;
|
||||
}
|
||||
NS_ASSERTION(mEntryCount == entryCount, "### mem cache badness");
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* nsMemoryCacheDeviceInfo - for implementing about:cache
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsMemoryCacheDeviceInfo, nsICacheDeviceInfo)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryCacheDeviceInfo::GetDescription(char ** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = NS_strdup("Memory cache device");
|
||||
if (!*result) return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryCacheDeviceInfo::GetUsageReport(char ** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
nsCString buffer;
|
||||
|
||||
buffer.AssignLiteral(" <tr>\n"
|
||||
" <th>Inactive storage:</th>\n"
|
||||
" <td>");
|
||||
buffer.AppendInt(mDevice->mInactiveSize / 1024);
|
||||
buffer.AppendLiteral(" KiB</td>\n"
|
||||
" </tr>\n");
|
||||
|
||||
*result = ToNewCString(buffer);
|
||||
if (!*result) return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryCacheDeviceInfo::GetEntryCount(uint32_t * result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
// XXX compare calculated count vs. mEntryCount
|
||||
*result = (uint32_t)mDevice->mEntryCount;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryCacheDeviceInfo::GetTotalSize(uint32_t * result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = (uint32_t)mDevice->mTotalSize;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryCacheDeviceInfo::GetMaximumSize(uint32_t * result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = (uint32_t)mDevice->mHardLimit;
|
||||
return NS_OK;
|
||||
}
|
||||
124
netwerk/cache/nsMemoryCacheDevice.h
vendored
Normal file
124
netwerk/cache/nsMemoryCacheDevice.h
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _nsMemoryCacheDevice_h_
|
||||
#define _nsMemoryCacheDevice_h_
|
||||
|
||||
#include "nsCacheDevice.h"
|
||||
#include "PLDHashTable.h"
|
||||
#include "nsCacheEntry.h"
|
||||
|
||||
|
||||
class nsMemoryCacheDeviceInfo;
|
||||
|
||||
/******************************************************************************
|
||||
* nsMemoryCacheDevice
|
||||
******************************************************************************/
|
||||
class nsMemoryCacheDevice : public nsCacheDevice
|
||||
{
|
||||
public:
|
||||
nsMemoryCacheDevice();
|
||||
virtual ~nsMemoryCacheDevice();
|
||||
|
||||
virtual nsresult Init();
|
||||
virtual nsresult Shutdown();
|
||||
|
||||
virtual const char * GetDeviceID(void);
|
||||
|
||||
virtual nsresult BindEntry( nsCacheEntry * entry );
|
||||
virtual nsCacheEntry * FindEntry( nsCString * key, bool *collision );
|
||||
virtual void DoomEntry( nsCacheEntry * entry );
|
||||
virtual nsresult DeactivateEntry( nsCacheEntry * entry );
|
||||
|
||||
virtual nsresult OpenInputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIInputStream ** result);
|
||||
|
||||
virtual nsresult OpenOutputStreamForEntry(nsCacheEntry * entry,
|
||||
nsCacheAccessMode mode,
|
||||
uint32_t offset,
|
||||
nsIOutputStream ** result);
|
||||
|
||||
virtual nsresult GetFileForEntry( nsCacheEntry * entry,
|
||||
nsIFile ** result );
|
||||
|
||||
virtual nsresult OnDataSizeChange( nsCacheEntry * entry, int32_t deltaSize );
|
||||
|
||||
virtual nsresult Visit( nsICacheVisitor * visitor );
|
||||
|
||||
virtual nsresult EvictEntries(const char * clientID);
|
||||
nsresult EvictPrivateEntries();
|
||||
|
||||
void SetCapacity(int32_t capacity);
|
||||
void SetMaxEntrySize(int32_t maxSizeInKilobytes);
|
||||
|
||||
bool EntryIsTooBig(int64_t entrySize);
|
||||
|
||||
size_t TotalSize();
|
||||
|
||||
private:
|
||||
friend class nsMemoryCacheDeviceInfo;
|
||||
enum { DELETE_ENTRY = true,
|
||||
DO_NOT_DELETE_ENTRY = false };
|
||||
|
||||
void AdjustMemoryLimits( int32_t softLimit, int32_t hardLimit);
|
||||
void EvictEntry( nsCacheEntry * entry , bool deleteEntry);
|
||||
void EvictEntriesIfNecessary();
|
||||
int EvictionList(nsCacheEntry * entry, int32_t deltaSize);
|
||||
|
||||
typedef bool (*EvictionMatcherFn)(nsCacheEntry* entry, void* args);
|
||||
nsresult DoEvictEntries(EvictionMatcherFn matchFn, void* args);
|
||||
|
||||
#ifdef DEBUG
|
||||
void CheckEntryCount();
|
||||
#endif
|
||||
/*
|
||||
* Data members
|
||||
*/
|
||||
enum {
|
||||
kQueueCount = 24 // entries > 2^23 (8Mb) start in last queue
|
||||
};
|
||||
|
||||
nsCacheEntryHashTable mMemCacheEntries;
|
||||
bool mInitialized;
|
||||
|
||||
PRCList mEvictionList[kQueueCount];
|
||||
|
||||
int32_t mHardLimit;
|
||||
int32_t mSoftLimit;
|
||||
|
||||
int32_t mTotalSize;
|
||||
int32_t mInactiveSize;
|
||||
|
||||
int32_t mEntryCount;
|
||||
int32_t mMaxEntryCount;
|
||||
int32_t mMaxEntrySize; // internal unit is bytes
|
||||
|
||||
// XXX what other stats do we want to keep?
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* nsMemoryCacheDeviceInfo - used to call nsIVisitor for about:cache
|
||||
******************************************************************************/
|
||||
class nsMemoryCacheDeviceInfo : public nsICacheDeviceInfo {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICACHEDEVICEINFO
|
||||
|
||||
explicit nsMemoryCacheDeviceInfo(nsMemoryCacheDevice* device)
|
||||
: mDevice(device)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsMemoryCacheDeviceInfo() {}
|
||||
nsMemoryCacheDevice* mDevice;
|
||||
};
|
||||
|
||||
|
||||
#endif // _nsMemoryCacheDevice_h_
|
||||
Loading…
Add table
Add a link
Reference in a new issue