Bug 367447. Support <link rel="offline-resource">. Patch by Dave Camp, r+sr=biesi

git-svn-id: svn://10.0.0.236/trunk@221859 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
roc+%cs.cmu.edu
2007-03-14 01:52:07 +00:00
parent 2da351febf
commit f427e2bbff
20 changed files with 855 additions and 102 deletions

View File

@@ -661,7 +661,12 @@ nsContentSink::ProcessLink(nsIContent* aElement,
PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
// prefetch href if relation is "next" or "prefetch"
if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
PrefetchHref(aHref, hasPrefetch);
PrefetchHref(aHref, hasPrefetch, PR_FALSE);
}
// fetch href into the offline cache if relation is "offline-resource"
if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
PrefetchHref(aHref, PR_TRUE, PR_TRUE);
}
// is it a stylesheet link?
@@ -746,7 +751,9 @@ nsContentSink::ProcessMETATag(nsIContent* aContent)
void
nsContentSink::PrefetchHref(const nsAString &aHref, PRBool aExplicit)
nsContentSink::PrefetchHref(const nsAString &aHref,
PRBool aExplicit,
PRBool aOffline)
{
//
// SECURITY CHECK: disable prefetching from mailnews!
@@ -789,12 +796,14 @@ nsContentSink::PrefetchHref(const nsAString &aHref, PRBool aExplicit)
charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
mDocumentBaseURI);
if (uri) {
prefetchService->PrefetchURI(uri, mDocumentURI, aExplicit);
if (aOffline)
prefetchService->PrefetchURIForOfflineUse(uri, mDocumentURI, aExplicit);
else
prefetchService->PrefetchURI(uri, mDocumentURI, aExplicit);
}
}
}
void
nsContentSink::ScrollToRef()
{

View File

@@ -165,7 +165,7 @@ protected:
const nsSubstring& aType,
const nsSubstring& aMedia);
void PrefetchHref(const nsAString &aHref, PRBool aExplicit);
void PrefetchHref(const nsAString &aHref, PRBool aExplicit, PRBool aOffline);
void ScrollToRef();
nsresult RefreshIfEnabled(nsIViewManager* vm);

View File

@@ -2996,7 +2996,14 @@ HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode)
nsAutoString hrefVal;
element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
if (!hrefVal.IsEmpty()) {
PrefetchHref(hrefVal, hasPrefetch);
PrefetchHref(hrefVal, hasPrefetch, PR_FALSE);
}
}
if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
nsAutoString hrefVal;
element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
if (!hrefVal.IsEmpty()) {
PrefetchHref(hrefVal, PR_TRUE, PR_TRUE);
}
}
}

View File

@@ -67,6 +67,11 @@ pref("browser.cache.memory.enable", true);
pref("browser.cache.disk_cache_ssl", false);
// 0 = once-per-session, 1 = each-time, 2 = never, 3 = when-appropriate/automatically
pref("browser.cache.check_doc_frequency", 3);
pref("browser.cache.offline.enable", true);
// offline cache capacity in kilobytes
pref("browser.cache.offline.capacity", 51200);
// Fastback caching - if this pref is negative, then we calculate the number
// of content viewers to cache based on the amount of available memory.
pref("browser.sessionhistory.max_total_viewers", -1);

View File

@@ -50,7 +50,7 @@ interface nsIFile;
* 3) Support for uniquely identifying cached data in cases when the URL
* is insufficient (e.g., HTTP form submission).
*/
[scriptable, uuid(b1f95f5e-ee05-4434-9d34-89a935d7feef)]
[scriptable, uuid(855bcc4d-0987-45b6-b138-3bf0bf407703)]
interface nsICachingChannel : nsISupports
{
/**
@@ -97,6 +97,14 @@ interface nsICachingChannel : nsISupports
*/
attribute boolean cacheAsFile;
/**
* Specifies whether or not the data should be placed in the offline cache,
* in addition to normal memory/disk caching. This may fail if the offline
* cache is not present. The value of this attribute should be set before
* opening the channel.
*/
attribute boolean cacheForOfflineUse;
/**
* Get the "file" where the cached data can be found. This is valid for
* as long as a reference to the cache token is held. This may return

View File

@@ -106,6 +106,8 @@ public:
return mContentSniffers.GetEntries();
}
PRBool IsOffline() { return mOffline; }
private:
// These shouldn't be called directly:
// - construct using GetInstance

View File

@@ -143,11 +143,14 @@ interface nsICache
* storage (ie. typically on a system's hard disk).
* STORE_ON_DISK_AS_FILE - Requires the cache entry to reside in persistent
* storage, and in a separate file.
* 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;
const nsCacheStoragePolicy STORE_ON_DISK_AS_FILE = 3;
const nsCacheStoragePolicy STORE_OFFLINE = 4;
/**
* All entries for a cache session are stored as streams of data or

View File

@@ -88,6 +88,8 @@ CPPSRCS += \
endif
endif
LOCAL_INCLUDES=-I$(srcdir)/../../base/src
include $(topsrcdir)/config/config.mk

View File

@@ -192,6 +192,11 @@ public:
(StoragePolicy() == nsICache::STORE_ON_DISK_AS_FILE);
}
PRBool IsAllowedOffline()
{
return (StoragePolicy() == nsICache::STORE_OFFLINE);
}
nsCacheStoragePolicy StoragePolicy()
{
return (nsCacheStoragePolicy)(mFlags & eStoragePolicyMask);

View File

@@ -60,6 +60,7 @@
#include "nsIPrefBranch.h"
#include "nsIPrefBranch2.h"
#include "nsILocalFile.h"
#include "nsIOService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsThreadUtils.h"
@@ -82,6 +83,11 @@
#define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
#define DISK_CACHE_CAPACITY 51200
#define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"
#define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"
#define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
#define OFFLINE_CACHE_CAPACITY 51200
#define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"
#define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"
#define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
@@ -97,6 +103,8 @@ public:
: mHaveProfile(PR_FALSE)
, mDiskCacheEnabled(PR_FALSE)
, mDiskCacheCapacity(0)
, mOfflineCacheEnabled(PR_FALSE)
, mOfflineCacheCapacity(0)
, mMemoryCacheEnabled(PR_TRUE)
, mMemoryCacheCapacity(-1)
{
@@ -111,6 +119,10 @@ public:
PRBool DiskCacheEnabled();
PRInt32 DiskCacheCapacity() { return mDiskCacheCapacity; }
nsILocalFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
PRBool OfflineCacheEnabled();
PRInt32 OfflineCacheCapacity() { return mOfflineCacheCapacity; }
nsILocalFile * OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory; }
PRBool MemoryCacheEnabled();
PRInt32 MemoryCacheCapacity();
@@ -121,6 +133,10 @@ private:
PRBool mDiskCacheEnabled;
PRInt32 mDiskCacheCapacity;
nsCOMPtr<nsILocalFile> mDiskCacheParentDirectory;
PRBool mOfflineCacheEnabled;
PRInt32 mOfflineCacheCapacity;
nsCOMPtr<nsILocalFile> mOfflineCacheParentDirectory;
PRBool mMemoryCacheEnabled;
PRInt32 mMemoryCacheCapacity;
@@ -159,6 +175,9 @@ nsCacheProfilePrefObserver::Install()
DISK_CACHE_ENABLE_PREF,
DISK_CACHE_CAPACITY_PREF,
DISK_CACHE_DIR_PREF,
OFFLINE_CACHE_ENABLE_PREF,
OFFLINE_CACHE_CAPACITY_PREF,
OFFLINE_CACHE_DIR_PREF,
MEMORY_CACHE_ENABLE_PREF,
MEMORY_CACHE_CAPACITY_PREF
};
@@ -210,6 +229,11 @@ nsCacheProfilePrefObserver::Remove()
prefs->RemoveObserver(DISK_CACHE_CAPACITY_PREF, this);
prefs->RemoveObserver(DISK_CACHE_DIR_PREF, this);
// remove Offline cache pref observers
prefs->RemoveObserver(OFFLINE_CACHE_ENABLE_PREF, this);
prefs->RemoveObserver(OFFLINE_CACHE_CAPACITY_PREF, this);
prefs->RemoveObserver(OFFLINE_CACHE_DIR_PREF, this);
// remove Memory cache pref observers
prefs->RemoveObserver(MEMORY_CACHE_ENABLE_PREF, this);
prefs->RemoveObserver(MEMORY_CACHE_CAPACITY_PREF, this);
@@ -278,7 +302,32 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
// XXX the next time the profile changes (browser launch)
#endif
} else
// which preference changed?
if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
&mOfflineCacheEnabled);
if (NS_FAILED(rv)) return rv;
nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
} else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
PRInt32 capacity = 0;
rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
if (NS_FAILED(rv)) return rv;
mOfflineCacheCapacity = PR_MAX(0, capacity);
nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
#if 0
} else if (!strcmp(DISK_OFFLINE_DIR_PREF, data.get())) {
// XXX We probaby don't want to respond to this pref except after
// XXX profile changes. Ideally, there should be some kind of user
// XXX notification that the pref change won't take effect until
// XXX the next time the profile changes (browser launch)
#endif
} else
#endif // !NECKO_DISK_CACHE
if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
@@ -356,6 +405,47 @@ nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
if (directory)
mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
}
// read offline cache device prefs
mOfflineCacheEnabled = PR_TRUE; // presume offline cache is enabled
(void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
&mOfflineCacheEnabled);
mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
(void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
&mOfflineCacheCapacity);
mOfflineCacheCapacity = PR_MAX(0, mOfflineCacheCapacity);
(void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF, // ignore error
NS_GET_IID(nsILocalFile),
getter_AddRefs(mOfflineCacheParentDirectory));
if (!mOfflineCacheParentDirectory) {
nsCOMPtr<nsIFile> directory;
// try to get the offline cache parent directory
rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
getter_AddRefs(directory));
if (NS_FAILED(rv)) {
// try to get the profile directory (there may not be a profile yet)
nsCOMPtr<nsIFile> profDir;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(profDir));
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
getter_AddRefs(directory));
if (!directory)
directory = profDir;
}
#if DEBUG
if (!directory) {
// use current process directory during development
rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
getter_AddRefs(directory));
}
#endif
if (directory)
mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
}
#endif // !NECKO_DISK_CACHE
// read memory cache device prefs
@@ -376,7 +466,17 @@ nsCacheProfilePrefObserver::DiskCacheEnabled()
return mDiskCacheEnabled;
}
PRBool
nsCacheProfilePrefObserver::OfflineCacheEnabled()
{
if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
return PR_FALSE;
return mOfflineCacheEnabled;
}
PRBool
nsCacheProfilePrefObserver::MemoryCacheEnabled()
{
@@ -472,6 +572,7 @@ nsCacheService::nsCacheService()
mEnableDiskDevice(PR_TRUE),
mMemoryDevice(nsnull),
mDiskDevice(nsnull),
mOfflineDevice(nsnull),
mTotalEntries(0),
mCacheHits(0),
mCacheMisses(0),
@@ -527,9 +628,10 @@ nsCacheService::Init()
NS_ADDREF(mObserver);
mObserver->Install();
mEnableDiskDevice = mObserver->DiskCacheEnabled();
mEnableMemoryDevice = mObserver->MemoryCacheEnabled();
mEnableDiskDevice = mObserver->DiskCacheEnabled();
mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
mEnableMemoryDevice = mObserver->MemoryCacheEnabled();
mInitialized = PR_TRUE;
return NS_OK;
}
@@ -561,10 +663,13 @@ nsCacheService::Shutdown()
delete mDiskDevice;
mDiskDevice = nsnull;
delete mOfflineDevice;
mOfflineDevice = nsnull;
#if defined(PR_LOGGING)
LogCacheStatistics();
#endif
#endif
#endif // !NECKO_DISK_CACHE
}
}
@@ -660,7 +765,19 @@ nsCacheService::EvictEntriesForClient(const char * clientID,
if (NS_FAILED(rv)) return rv;
}
}
#endif // !NECKO_DISK_CACHE
if (storagePolicy == nsICache::STORE_ANYWHERE ||
storagePolicy == nsICache::STORE_OFFLINE) {
if (mEnableOfflineDevice) {
if (!mOfflineDevice) {
rv = CreateOfflineDevice();
if (NS_FAILED(rv)) return rv;
}
rv = mOfflineDevice->EvictEntries(clientID);
if (NS_FAILED(rv)) return rv;
}
}
#endif // ! NECKO_DISK_CACHE
if (storagePolicy == nsICache::STORE_ANYWHERE ||
storagePolicy == nsICache::STORE_IN_MEMORY) {
@@ -702,6 +819,10 @@ nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePo
storagePolicy == nsICache::STORE_ON_DISK_AS_FILE)) {
return PR_TRUE;
}
if (gService->mEnableOfflineDevice &&
storagePolicy == nsICache::STORE_OFFLINE) {
return PR_TRUE;
}
return PR_FALSE;
}
@@ -735,6 +856,15 @@ NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
rv = mDiskDevice->Visit(visitor);
if (NS_FAILED(rv)) return rv;
}
if (mEnableOfflineDevice) {
if (!mOfflineDevice) {
rv = CreateOfflineDevice();
if (NS_FAILED(rv)) return rv;
}
rv = mOfflineDevice->Visit(visitor);
if (NS_FAILED(rv)) return rv;
}
#endif // !NECKO_DISK_CACHE
// XXX notify any shutdown process that visitation is complete for THIS visitor.
@@ -786,6 +916,39 @@ nsCacheService::CreateDiskDevice()
#endif
}
nsresult
nsCacheService::CreateOfflineDevice()
{
#ifdef NECKO_DISK_CACHE
CACHE_LOG_ALWAYS(("Creating offline device"));
// XXX: want a sql-based device
if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
if (mOfflineDevice) return NS_OK;
mOfflineDevice = new nsDiskCacheDevice;
if (!mOfflineDevice) return NS_ERROR_OUT_OF_MEMORY;
// set the preferences
mOfflineDevice->SetCacheParentDirectoryAndName(mObserver->OfflineCacheParentDirectory(),
NS_LITERAL_CSTRING("OfflineCache"));
mOfflineDevice->SetCapacity(mObserver->OfflineCacheCapacity());
nsresult rv = mOfflineDevice->Init();
if (NS_FAILED(rv)) {
CACHE_LOG_DEBUG(("mOfflineDevice->Init() failed (0x%.8x)\n", rv));
CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
mEnableOfflineDevice = PR_FALSE;
delete mOfflineDevice;
mOfflineDevice = nsnull;
}
return rv;
#else // !NECKO_DISK_CACHE
NS_NOTREACHED("nsCacheService::CreateOfflineDevice");
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult
nsCacheService::CreateMemoryDevice()
@@ -1115,6 +1278,23 @@ nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy,
#endif // !NECKO_DISK_CACHE
}
if (!entry && (policy == nsICache::STORE_OFFLINE ||
(policy == nsICache::STORE_ANYWHERE &&
gIOService->IsOffline()))) {
#ifdef NECKO_DISK_CACHE
if (mEnableOfflineDevice) {
if (!mOfflineDevice) {
nsresult rv = CreateOfflineDevice();
if (NS_FAILED(rv))
return nsnull;
}
entry = mOfflineDevice->FindEntry(key, collision);
}
#endif
}
return entry;
}
@@ -1156,6 +1336,23 @@ nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
}
}
#ifdef NECKO_DISK_CACHE
if (!device && entry->IsStreamData() &&
entry->IsAllowedOffline() && mEnableOfflineDevice) {
if (!mOfflineDevice) {
(void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
}
if (mOfflineDevice) {
entry->MarkBinding();
nsresult rv = mOfflineDevice->BindEntry(entry);
entry->ClearBinding();
if (NS_SUCCEEDED(rv))
device = mOfflineDevice;
}
}
#endif // ! NECKO_DISK_CACHE
if (device)
entry->SetCacheDevice(device);
return device;
@@ -1219,6 +1416,14 @@ nsCacheService::OnProfileShutdown(PRBool cleanse)
gService->mDiskDevice->Shutdown();
gService->mEnableDiskDevice = PR_FALSE;
}
if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
if (cleanse)
gService->mOfflineDevice->EvictEntries(nsnull);
gService->mOfflineDevice->Shutdown();
gService->mEnableOfflineDevice = PR_FALSE;
}
#endif // !NECKO_DISK_CACHE
if (gService->mMemoryDevice) {
@@ -1236,9 +1441,10 @@ nsCacheService::OnProfileChanged()
nsCacheServiceAutoLock lock;
gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
#ifdef NECKO_DISK_CACHE
if (gService->mDiskDevice) {
gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
@@ -1252,6 +1458,19 @@ nsCacheService::OnProfileChanged()
// XXX delete mDiskDevice?
}
}
if (gService->mOfflineDevice) {
gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
// XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
nsresult rv = gService->mOfflineDevice->Init();
if (NS_FAILED(rv)) {
NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
gService->mEnableOfflineDevice = PR_FALSE;
// XXX delete mOfflineDevice?
}
}
#endif // !NECKO_DISK_CACHE
// If memoryDevice exists, reset its size to the new profile
@@ -1292,6 +1511,29 @@ nsCacheService::SetDiskCacheCapacity(PRInt32 capacity)
gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
}
void
nsCacheService::SetOfflineCacheEnabled(PRBool enabled)
{
if (!gService) return;
nsCacheServiceAutoLock lock;
gService->mEnableOfflineDevice = enabled;
}
void
nsCacheService::SetOfflineCacheCapacity(PRInt32 capacity)
{
if (!gService) return;
nsCacheServiceAutoLock lock;
#ifdef NECKO_DISK_CACHE
if (gService->mOfflineDevice) {
gService->mOfflineDevice->SetCapacity(capacity);
}
#endif // !NECKO_DISK_CACHE
gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
}
void
nsCacheService::SetMemoryCache()

View File

@@ -150,6 +150,9 @@ public:
static void SetDiskCacheEnabled(PRBool enabled);
static void SetDiskCacheCapacity(PRInt32 capacity);
static void SetOfflineCacheEnabled(PRBool enabled);
static void SetOfflineCacheCapacity(PRInt32 capacity);
static void SetMemoryCache();
nsresult Init();
@@ -165,6 +168,7 @@ private:
static void Unlock();
nsresult CreateDiskDevice();
nsresult CreateOfflineDevice();
nsresult CreateMemoryDevice();
nsresult CreateRequest(nsCacheSession * session,
@@ -240,9 +244,11 @@ private:
PRBool mEnableMemoryDevice;
PRBool mEnableDiskDevice;
PRBool mEnableOfflineDevice;
nsMemoryCacheDevice * mMemoryDevice;
nsDiskCacheDevice * mDiskDevice;
nsDiskCacheDevice * mOfflineDevice;
nsCacheEntryHashTable mActiveEntries;
PRCList mDoomedEntries;

View File

@@ -929,6 +929,43 @@ nsDiskCacheDevice::SetCacheParentDirectory(nsILocalFile * parentDir)
mCacheDirectory = do_QueryInterface(directory);
}
// XXX: This is here to support the offline cache, and can be removed
// XXX: once it has its own cache implementation
void
nsDiskCacheDevice::SetCacheParentDirectoryAndName(nsILocalFile * parentDir,
const nsACString & str)
{
nsresult rv;
PRBool exists;
if (Initialized()) {
NS_ASSERTION(PR_FALSE, "Cannot switch cache directory when initialized");
return;
}
if (!parentDir) {
mCacheDirectory = nsnull;
return;
}
// ensure parent directory exists
rv = parentDir->Exists(&exists);
if (NS_SUCCEEDED(rv) && !exists)
rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_FAILED(rv)) return;
// ensure cache directory exists
nsCOMPtr<nsIFile> directory;
rv = parentDir->Clone(getter_AddRefs(directory));
if (NS_FAILED(rv)) return;
rv = directory->AppendNative(str);
if (NS_FAILED(rv)) return;
mCacheDirectory = do_QueryInterface(directory);
}
void
nsDiskCacheDevice::getCacheDirectory(nsILocalFile ** result)

View File

@@ -93,6 +93,10 @@ public:
void SetCacheParentDirectory(nsILocalFile * parentDir);
void SetCapacity(PRUint32 capacity);
// XXX: This is here to support the offline cache, and can be removed
// XXX: once it has its own cache implementation
void SetCacheParentDirectoryAndName(nsILocalFile * parentDir,
const nsACString & str);
/* private: */

View File

@@ -118,6 +118,7 @@ nsHttpChannel::nsHttpChannel()
, mSuppressDefensiveAuth(PR_FALSE)
, mResuming(PR_FALSE)
, mInitedCacheEntry(PR_FALSE)
, mCacheForOfflineUse(PR_FALSE)
{
LOG(("Creating nsHttpChannel @%x\n", this));
@@ -267,14 +268,9 @@ nsHttpChannel::Connect(PRBool firstTime)
// true when called from AsyncOpen
if (firstTime) {
PRBool delayed = PR_FALSE;
PRBool offline = PR_FALSE;
// are we offline?
nsCOMPtr<nsIIOService> ioService;
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
if (NS_FAILED(rv)) return rv;
ioService->GetOffline(&offline);
// are we offline?
PRBool offline = gIOService->IsOffline();
if (offline)
mLoadFlags |= LOAD_ONLY_FROM_CACHE;
else if (PL_strcmp(mConnectionInfo->ProxyType(), "unknown") == 0)
@@ -297,7 +293,14 @@ nsHttpChannel::Connect(PRBool firstTime)
return NS_ERROR_DOCUMENT_NOT_CACHED;
// otherwise, let's just proceed without using the cache.
}
// if cacheForOfflineUse has been set, open up an offline cache
// entry to update
if (mCacheForOfflineUse) {
rv = OpenOfflineCacheEntryForWriting();
if (NS_FAILED(rv)) return rv;
}
if (NS_SUCCEEDED(rv) && delayed)
return NS_OK;
}
@@ -785,6 +788,12 @@ nsHttpChannel::ProcessResponse()
if (NS_SUCCEEDED(rv)) {
InitCacheEntry();
CloseCacheEntry();
if (mCacheForOfflineUse) {
// Store response in the offline cache
InitOfflineCacheEntry();
CloseOfflineCacheEntry();
}
}
else {
LOG(("ProcessRedirection failed [rv=%x]\n", rv));
@@ -884,8 +893,30 @@ nsHttpChannel::ProcessNormal()
if (NS_FAILED(rv)) return rv;
// install cache listener if we still have a cache entry open
if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE))
if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE)) {
rv = InstallCacheListener();
if (NS_FAILED(rv)) return rv;
}
// create offline cache entry if offline caching was requested
if (mCacheForOfflineUse) {
PRBool shouldCacheForOfflineUse;
rv = ShouldUpdateOfflineCacheEntry(&shouldCacheForOfflineUse);
if (NS_FAILED(rv)) return rv;
if (shouldCacheForOfflineUse) {
LOG(("writing to the offline cache"));
rv = InitOfflineCacheEntry();
if (NS_FAILED(rv)) return rv;
if (mOfflineCacheEntry) {
rv = InstallOfflineCacheListener();
if (NS_FAILED(rv)) return rv;
}
} else {
LOG(("offline cache is up to date, not updating"));
CloseOfflineCacheEntry();
}
}
return rv;
}
@@ -1262,10 +1293,6 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
storagePolicy = nsICache::STORE_IN_MEMORY;
else
storagePolicy = nsICache::STORE_ANYWHERE; // allow on disk
nsCOMPtr<nsICacheSession> session;
rv = gHttpHandler->GetCacheSession(storagePolicy,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
// Set the desired cache access mode accordingly...
nsCacheAccessMode accessRequested;
@@ -1281,11 +1308,30 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
else
accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing
nsCOMPtr<nsICacheSession> session;
rv = gHttpHandler->GetCacheSession(storagePolicy,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
// we'll try to synchronously open the cache entry... however, it may be
// in use and not yet validated, in which case we'll try asynchronously
// opening the cache entry.
rv = session->OpenCacheEntry(cacheKey, accessRequested, PR_FALSE,
getter_AddRefs(mCacheEntry));
if (offline &&
!(NS_SUCCEEDED(rv) || rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)) {
// couldn't find it in the main cache, check the offline cache
storagePolicy = nsICache::STORE_OFFLINE;
rv = gHttpHandler->GetCacheSession(storagePolicy,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
rv = session->OpenCacheEntry(cacheKey, accessRequested, PR_FALSE,
getter_AddRefs(mCacheEntry));
}
if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
// access to the cache entry has been denied (because the cache entry
// is probably in use by another channel).
@@ -1305,6 +1351,66 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
return rv;
}
nsresult
nsHttpChannel::OpenOfflineCacheEntryForWriting()
{
nsresult rv;
LOG(("nsHttpChannel::OpenOfflineCacheEntryForWriting [this=%x]", this));
// make sure we're not abusing this function
NS_PRECONDITION(!mOfflineCacheEntry, "cache entry already open");
PRBool offline = gIOService->IsOffline();
if (offline) {
// only put things in the offline cache while online
return NS_OK;
}
if (mRequestHead.Method() != nsHttp::Get) {
// only cache complete documents offline
return NS_OK;
}
if (mRequestHead.PeekHeader(nsHttp::Range)) {
// we don't support caching for byte range requests initiated
// by our clients or via nsIResumableChannel.
return NS_OK;
}
if (RequestIsConditional()) {
// don't use the cache if our consumer is making a conditional request
// (see bug 331825).
return NS_OK;
}
nsCAutoString cacheKey;
GenerateCacheKey(cacheKey);
nsCOMPtr<nsICacheSession> session;
rv = gHttpHandler->GetCacheSession(nsICache::STORE_OFFLINE,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
PR_FALSE, getter_AddRefs(mOfflineCacheEntry));
if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
// access to the cache entry has been denied (because the cache entry
// is probably in use by another channel). Either the cache is being
// read from (we're offline) or it's being updated elsewhere.
return NS_OK;
}
if (NS_SUCCEEDED(rv)) {
mOfflineCacheEntry->GetAccessGranted(&mOfflineCacheAccess);
LOG(("got offline cache entry [access=%x]\n", mOfflineCacheAccess));
}
return rv;
}
nsresult
nsHttpChannel::GenerateCacheKey(nsACString &cacheKey)
{
@@ -1597,6 +1703,48 @@ nsHttpChannel::CheckCache()
return NS_OK;
}
nsresult
nsHttpChannel::ShouldUpdateOfflineCacheEntry(PRBool *shouldCacheForOfflineUse)
{
*shouldCacheForOfflineUse = PR_FALSE;
if (!mOfflineCacheEntry) {
return NS_OK;
}
// if we're updating the cache entry, update the offline cache entry too
if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE)) {
*shouldCacheForOfflineUse = PR_TRUE;
return NS_OK;
}
// if there's nothing in the offline cache, add it
if (mOfflineCacheEntry && (mOfflineCacheAccess == nsICache::ACCESS_WRITE)) {
*shouldCacheForOfflineUse = PR_TRUE;
return NS_OK;
}
// if the document is newer than the offline entry, update it
PRUint32 docLastModifiedTime;
nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime);
if (NS_FAILED(rv)) {
*shouldCacheForOfflineUse = PR_TRUE;
return NS_OK;
}
PRUint32 offlineLastModifiedTime;
rv = mOfflineCacheEntry->GetLastModified(&offlineLastModifiedTime);
NS_ENSURE_SUCCESS(rv, rv);
if (docLastModifiedTime > offlineLastModifiedTime) {
*shouldCacheForOfflineUse = PR_TRUE;
return NS_OK;
}
return NS_OK;
}
// If the data in the cache hasn't expired, then there's no need to
// talk with the server, not even to do an if-modified-since. This
// method creates a stream from the cache, synthesizing all the various
@@ -1641,8 +1789,18 @@ nsHttpChannel::ReadFromCache()
// have we been configured to skip reading from the cache?
if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
LOG(("skipping read from cache based on LOAD_ONLY_IF_MODIFIED load flag\n"));
return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
// if offline caching has been requested and the offline cache needs
// updating, complete the call even if the main cache entry is
// up-to-date
PRBool shouldUpdateOffline;
if (!mCacheForOfflineUse ||
NS_FAILED(ShouldUpdateOfflineCacheEntry(&shouldUpdateOffline)) ||
!shouldUpdateOffline) {
LOG(("skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
"load flag\n"));
return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
}
}
// open input stream for reading...
@@ -1697,6 +1855,24 @@ nsHttpChannel::CloseCacheEntry()
mInitedCacheEntry = PR_FALSE;
}
void
nsHttpChannel::CloseOfflineCacheEntry()
{
if (!mOfflineCacheEntry)
return;
LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%x]", this));
if (NS_FAILED(mStatus)) {
mOfflineCacheEntry->Doom();
}
mOfflineCacheEntry = 0;
mOfflineCacheAccess = 0;
}
// Initialize the cache entry for writing.
// - finalize storage policy
// - store security info
@@ -1731,21 +1907,52 @@ nsHttpChannel::InitCacheEntry()
if (NS_FAILED(rv)) return rv;
}
// Store secure data in memory only
if (mSecurityInfo)
mCacheEntry->SetSecurityInfo(mSecurityInfo);
// Set the expiration time for this cache entry
rv = UpdateExpirationTime();
if (NS_FAILED(rv)) return rv;
rv = AddCacheEntryHeaders(mCacheEntry);
if (NS_FAILED(rv)) return rv;
mInitedCacheEntry = PR_TRUE;
return NS_OK;
}
nsresult
nsHttpChannel::InitOfflineCacheEntry()
{
if (!mOfflineCacheEntry) {
return NS_OK;
}
if (mResponseHead->NoStore()) {
CloseOfflineCacheEntry();
return NS_OK;
}
return AddCacheEntryHeaders(mOfflineCacheEntry);
}
nsresult
nsHttpChannel::AddCacheEntryHeaders(nsICacheEntryDescriptor *entry)
{
nsresult rv;
// Store secure data in memory only
if (mSecurityInfo)
entry->SetSecurityInfo(mSecurityInfo);
// Store the HTTP request method with the cache entry so we can distinguish
// for example GET and HEAD responses.
rv = mCacheEntry->SetMetaDataElement("request-method", mRequestHead.Method().get());
rv = entry->SetMetaDataElement("request-method",
mRequestHead.Method().get());
if (NS_FAILED(rv)) return rv;
// Store the HTTP authorization scheme used if any...
rv = StoreAuthorizationMetaData();
rv = StoreAuthorizationMetaData(entry);
if (NS_FAILED(rv)) return rv;
// Iterate over the headers listed in the Vary response header, and
@@ -1773,7 +1980,7 @@ nsHttpChannel::InitCacheEntry()
if (requestVal) {
// build cache meta data key and set meta data element...
metaKey = prefix + nsDependentCString(token);
mCacheEntry->SetMetaDataElement(metaKey.get(), requestVal);
entry->SetMetaDataElement(metaKey.get(), requestVal);
}
}
token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
@@ -1786,22 +1993,20 @@ nsHttpChannel::InitCacheEntry()
// the meta data.
nsCAutoString head;
mResponseHead->Flatten(head, PR_TRUE);
rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
if (NS_FAILED(rv)) return rv;
rv = entry->SetMetaDataElement("response-head", head.get());
mInitedCacheEntry = PR_TRUE;
return NS_OK;
return rv;
}
nsresult
nsHttpChannel::StoreAuthorizationMetaData()
nsHttpChannel::StoreAuthorizationMetaData(nsICacheEntryDescriptor *entry)
{
// Not applicable to proxy authorization...
const char *val = mRequestHead.PeekHeader(nsHttp::Authorization);
if (val) {
// eg. [Basic realm="wally world"]
nsCAutoString buf(Substring(val, strchr(val, ' ')));
return mCacheEntry->SetMetaDataElement("auth", buf.get());
return entry->SetMetaDataElement("auth", buf.get());
}
return NS_OK;
}
@@ -1857,6 +2062,33 @@ nsHttpChannel::InstallCacheListener(PRUint32 offset)
return NS_OK;
}
nsresult
nsHttpChannel::InstallOfflineCacheListener()
{
nsresult rv;
LOG(("Preparing to write data into the offline cache [uri=%s]\n",
mSpec.get()));
NS_ASSERTION(mOfflineCacheEntry, "no offline cache entry");
NS_ASSERTION(mListener, "no listener");
nsCOMPtr<nsIOutputStream> out;
rv = mOfflineCacheEntry->OpenOutputStream(0, getter_AddRefs(out));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIStreamListenerTee> tee =
do_CreateInstance(kStreamListenerTeeCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = tee->Init(mListener, out);
if (NS_FAILED(rv)) return rv;
mListener = tee;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel <redirect>
//-----------------------------------------------------------------------------
@@ -4059,6 +4291,9 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
if (mCacheEntry)
CloseCacheEntry();
if (mOfflineCacheEntry)
CloseOfflineCacheEntry();
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, status);
@@ -4257,6 +4492,23 @@ nsHttpChannel::SetCacheAsFile(PRBool value)
return mCacheEntry->SetStoragePolicy(policy);
}
NS_IMETHODIMP
nsHttpChannel::GetCacheForOfflineUse(PRBool *value)
{
*value = mCacheForOfflineUse;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetCacheForOfflineUse(PRBool value)
{
mCacheForOfflineUse = value;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetCacheFile(nsIFile **cacheFile)
{

View File

@@ -173,15 +173,21 @@ private:
// cache specific methods
nsresult OpenCacheEntry(PRBool offline, PRBool *delayed);
nsresult OpenOfflineCacheEntryForWriting();
nsresult GenerateCacheKey(nsACString &key);
nsresult UpdateExpirationTime();
nsresult CheckCache();
nsresult ShouldUpdateOfflineCacheEntry(PRBool *shouldCacheForOfflineUse);
nsresult ReadFromCache();
void CloseCacheEntry();
void CloseOfflineCacheEntry();
nsresult InitCacheEntry();
nsresult StoreAuthorizationMetaData();
nsresult InitOfflineCacheEntry();
nsresult AddCacheEntryHeaders(nsICacheEntryDescriptor *entry);
nsresult StoreAuthorizationMetaData(nsICacheEntryDescriptor *entry);
nsresult FinalizeCacheEntry();
nsresult InstallCacheListener(PRUint32 offset = 0);
nsresult InstallOfflineCacheListener();
// byte range request specific methods
nsresult SetupByteRangeRequest(PRUint32 partialLen);
@@ -247,6 +253,9 @@ private:
PRUint32 mPostID;
PRUint32 mRequestTime;
nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
nsCacheAccessMode mOfflineCacheAccess;
// auth specific data
nsISupports *mProxyAuthContinuationState;
nsCString mProxyAuthType;
@@ -276,6 +285,7 @@ private:
PRUint32 mSuppressDefensiveAuth : 1;
PRUint32 mResuming : 1;
PRUint32 mInitedCacheEntry : 1;
PRUint32 mCacheForOfflineUse : 1;
class nsContentEncodings : public nsIUTF8StringEnumerator
{

View File

@@ -428,10 +428,21 @@ nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy,
rv = mCacheSession_MEM->SetDoomEntriesIfExpired(PR_FALSE);
if (NS_FAILED(rv)) return rv;
rv = serv->CreateSession("HTTP-offline",
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(mCacheSession_OFFLINE));
if (NS_FAILED(rv)) return rv;
rv = mCacheSession_OFFLINE->SetDoomEntriesIfExpired(PR_FALSE);
if (NS_FAILED(rv)) return rv;
}
if (storagePolicy == nsICache::STORE_IN_MEMORY)
NS_ADDREF(*result = mCacheSession_MEM);
else if (storagePolicy == nsICache::STORE_OFFLINE)
NS_ADDREF(*result = mCacheSession_OFFLINE);
else
NS_ADDREF(*result = mCacheSession_ANY);

View File

@@ -269,6 +269,7 @@ private:
// cache support
nsCOMPtr<nsICacheSession> mCacheSession_ANY;
nsCOMPtr<nsICacheSession> mCacheSession_MEM;
nsCOMPtr<nsICacheSession> mCacheSession_OFFLINE;
PRUint32 mLastUniqueID;
PRUint32 mSessionStartTime;

View File

@@ -39,7 +39,7 @@
interface nsIURI;
[scriptable, uuid(933cb52a-2864-4a40-8678-a2d0851b0ef4)]
[scriptable, uuid(ae241be6-3cb6-47ec-9e38-2d89bc22042a)]
interface nsIPrefetchService : nsISupports
{
/**
@@ -51,5 +51,17 @@ interface nsIPrefetchService : nsISupports
*/
void prefetchURI(in nsIURI aURI, in nsIURI aReferrerURI, in boolean aExplicit);
/**
* Enqueue a request to prefetch the specified URI, making sure it's in the
* offline cache.
*
* @param aURI the URI of the document to prefetch
* @param aReferrerURI the URI of the referring page
* @param aExplicit the link element has an explicit offline link type
*/
void prefetchURIForOfflineUse(in nsIURI aURI,
in nsIURI aReferrerURI,
in boolean aExplicit);
// XXX do we need a way to cancel prefetch requests?
};

View File

@@ -144,34 +144,44 @@ nsPrefetchListener::OnStartRequest(nsIRequest *aRequest,
{
nsresult rv;
nsCOMPtr<nsICachingChannel> cachingChannel(do_QueryInterface(aRequest, &rv));
nsCOMPtr<nsICachingChannel> cachingChannel =
do_QueryInterface(aRequest, &rv);
if (NS_FAILED(rv)) return rv;
// no need to prefetch a document that is already in the cache
PRBool fromCache;
if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) && fromCache) {
LOG(("document is already in the cache; canceling prefetch\n"));
return NS_BINDING_ABORTED;
}
//
// no need to prefetch a document that must be requested fresh each
// and every time.
//
nsCOMPtr<nsISupports> cacheToken;
cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
if (!cacheToken)
return NS_ERROR_ABORT; // bail, no cache entry
nsCOMPtr<nsICacheEntryInfo> entryInfo(do_QueryInterface(cacheToken, &rv));
PRBool cacheForOfflineUse;
rv = cachingChannel->GetCacheForOfflineUse(&cacheForOfflineUse);
if (NS_FAILED(rv)) return rv;
PRUint32 expTime;
if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) {
if (NowInSeconds() >= expTime) {
LOG(("document cannot be reused from cache; canceling prefetch\n"));
if (!cacheForOfflineUse) {
// no need to prefetch a document that is already in the cache
PRBool fromCache;
if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) &&
fromCache) {
LOG(("document is already in the cache; canceling prefetch\n"));
return NS_BINDING_ABORTED;
}
//
// no need to prefetch a document that must be requested fresh each
// and every time.
//
nsCOMPtr<nsISupports> cacheToken;
cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
if (!cacheToken)
return NS_ERROR_ABORT; // bail, no cache entry
nsCOMPtr<nsICacheEntryInfo> entryInfo =
do_QueryInterface(cacheToken, &rv);
if (NS_FAILED(rv)) return rv;
PRUint32 expTime;
if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) {
if (NowInSeconds() >= expTime) {
LOG(("document cannot be reused from cache; "
"canceling prefetch\n"));
return NS_BINDING_ABORTED;
}
}
}
return NS_OK;
}
@@ -230,11 +240,26 @@ nsPrefetchListener::OnChannelRedirect(nsIChannel *aOldChannel,
if (NS_FAILED(rv))
return rv;
PRBool offline;
nsCOMPtr<nsICachingChannel> oldCachingChannel =
do_QueryInterface(aOldChannel);
if (NS_SUCCEEDED(oldCachingChannel->GetCacheForOfflineUse(&offline)) &&
offline) {
nsCOMPtr<nsICachingChannel> newCachingChannel =
do_QueryInterface(aOldChannel);
if (newCachingChannel)
newCachingChannel->SetCacheForOfflineUse(PR_TRUE);
}
PRBool match;
rv = newURI->SchemeIs("http", &match);
if (NS_FAILED(rv) || !match) {
LOG(("rejected: URL is not of type http\n"));
return NS_ERROR_ABORT;
if (!offline ||
NS_FAILED(newURI->SchemeIs("https", &match)) ||
!match) {
LOG(("rejected: URL is not of type http\n"));
return NS_ERROR_ABORT;
}
}
// HTTP request headers are not automatically forwarded to the new channel.
@@ -242,7 +267,10 @@ nsPrefetchListener::OnChannelRedirect(nsIChannel *aOldChannel,
NS_ENSURE_STATE(httpChannel);
httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
NS_LITERAL_CSTRING("prefetch"), PR_FALSE);
offline ?
NS_LITERAL_CSTRING("offline-resource") :
NS_LITERAL_CSTRING("prefetch"),
PR_FALSE);
mService->UpdateCurrentChannel(aNewChannel);
return NS_OK;
@@ -264,7 +292,7 @@ nsPrefetchService::~nsPrefetchService()
{
// cannot reach destructor if prefetch in progress (listener owns reference
// to this service)
EmptyQueue();
EmptyQueue(PR_TRUE);
}
nsresult
@@ -314,7 +342,9 @@ nsPrefetchService::ProcessNextURI()
if (!listener) return;
do {
rv = DequeueURI(getter_AddRefs(uri), getter_AddRefs(referrer));
PRBool offline;
rv = DequeueURI(getter_AddRefs(uri), getter_AddRefs(referrer),
&offline);
if (NS_FAILED(rv)) break;
#if defined(PR_LOGGING)
@@ -335,12 +365,26 @@ nsPrefetchService::ProcessNextURI()
if (NS_FAILED(rv)) continue;
// configure HTTP specific stuff
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mCurrentChannel));
nsCOMPtr<nsIHttpChannel> httpChannel =
do_QueryInterface(mCurrentChannel);
if (httpChannel) {
httpChannel->SetReferrer(referrer);
httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
NS_LITERAL_CSTRING("prefetch"),
PR_FALSE);
httpChannel->SetRequestHeader(
NS_LITERAL_CSTRING("X-Moz"),
offline ?
NS_LITERAL_CSTRING("offline-resource") :
NS_LITERAL_CSTRING("prefetch"),
PR_FALSE);
}
if (offline) {
nsCOMPtr<nsICachingChannel> cachingChannel =
do_QueryInterface(mCurrentChannel);
if (cachingChannel) {
if (NS_FAILED(cachingChannel->SetCacheForOfflineUse(PR_TRUE))) {
continue;
}
}
}
rv = mCurrentChannel->AsyncOpen(listener, nsnull);
@@ -371,9 +415,11 @@ nsPrefetchService::RemoveProgressListener()
}
nsresult
nsPrefetchService::EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI)
nsPrefetchService::EnqueueURI(nsIURI *aURI,
nsIURI *aReferrerURI,
PRBool aOffline)
{
nsPrefetchNode *node = new nsPrefetchNode(aURI, aReferrerURI);
nsPrefetchNode *node = new nsPrefetchNode(aURI, aReferrerURI, aOffline);
if (!node)
return NS_ERROR_OUT_OF_MEMORY;
@@ -390,7 +436,9 @@ nsPrefetchService::EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI)
}
nsresult
nsPrefetchService::DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI)
nsPrefetchService::DequeueURI(nsIURI **aURI,
nsIURI **aReferrerURI,
PRBool *aOffline)
{
if (!mQueueHead)
return NS_ERROR_NOT_AVAILABLE;
@@ -398,6 +446,7 @@ nsPrefetchService::DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI)
// remove from the head
NS_ADDREF(*aURI = mQueueHead->mURI);
NS_ADDREF(*aReferrerURI = mQueueHead->mReferrerURI);
*aOffline = mQueueHead->mOffline;
nsPrefetchNode *node = mQueueHead;
mQueueHead = mQueueHead->mNext;
@@ -410,16 +459,25 @@ nsPrefetchService::DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI)
}
void
nsPrefetchService::EmptyQueue()
nsPrefetchService::EmptyQueue(PRBool includeOffline)
{
nsresult rv;
nsCOMPtr<nsIURI> uri, referrer;
nsPrefetchNode *prev = 0;
nsPrefetchNode *node = mQueueHead;
do {
rv = DequeueURI(getter_AddRefs(uri),
getter_AddRefs(referrer));
while (node) {
nsPrefetchNode *next = node->mNext;
if (includeOffline || !node->mOffline) {
if (prev)
prev->mNext = next;
else
mQueueHead = next;
delete node;
}
else
prev = node;
node = next;
}
while (NS_SUCCEEDED(rv));
}
void
@@ -453,9 +511,27 @@ nsPrefetchService::StopPrefetching()
if (!mCurrentChannel)
return;
// if it's an offline prefetch, requeue it for when prefetching starts
// again
nsCOMPtr<nsICachingChannel> cachingChannel =
do_QueryInterface(mCurrentChannel);
PRBool offline;
if (cachingChannel &&
NS_SUCCEEDED(cachingChannel->GetCacheForOfflineUse(&offline)) &&
offline) {
nsCOMPtr<nsIHttpChannel> httpChannel =
do_QueryInterface(mCurrentChannel);
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIURI> referrerURI;
if (NS_SUCCEEDED(mCurrentChannel->GetURI(getter_AddRefs(uri))) &&
NS_SUCCEEDED(httpChannel->GetReferrer(getter_AddRefs(referrerURI)))) {
EnqueueURI(uri, referrerURI, PR_TRUE);
}
}
mCurrentChannel->Cancel(NS_BINDING_ABORTED);
mCurrentChannel = nsnull;
EmptyQueue();
EmptyQueue(PR_FALSE);
}
//-----------------------------------------------------------------------------
@@ -469,11 +545,14 @@ NS_IMPL_ISUPPORTS4(nsPrefetchService,
nsISupportsWeakReference)
//-----------------------------------------------------------------------------
// nsPrefetchService::nsIPretetchService
// nsPrefetchService::nsIPrefetchService
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsPrefetchService::PrefetchURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aExplicit)
nsresult
nsPrefetchService::Prefetch(nsIURI *aURI,
nsIURI *aReferrerURI,
PRBool aExplicit,
PRBool aOffline)
{
nsresult rv;
@@ -507,8 +586,15 @@ nsPrefetchService::PrefetchURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aExpli
PRBool match;
rv = aURI->SchemeIs("http", &match);
if (NS_FAILED(rv) || !match) {
LOG(("rejected: URL is not of type http\n"));
return NS_ERROR_ABORT;
if (aOffline) {
// Offline https urls can be prefetched
rv = aURI->SchemeIs("https", &match);
}
if (NS_FAILED(rv) || !match) {
LOG(("rejected: URL is not of type http\n"));
return NS_ERROR_ABORT;
}
}
//
@@ -516,8 +602,15 @@ nsPrefetchService::PrefetchURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aExpli
//
rv = aReferrerURI->SchemeIs("http", &match);
if (NS_FAILED(rv) || !match) {
LOG(("rejected: referrer URL is not of type http\n"));
return NS_ERROR_ABORT;
if (aOffline) {
// Offline https urls can be prefetched
rv = aURI->SchemeIs("https", &match);
}
if (NS_FAILED(rv) || !match) {
LOG(("rejected: referrer URL is not of type http\n"));
return NS_ERROR_ABORT;
}
}
// skip URLs that contain query strings, except URLs for which prefetching
@@ -542,8 +635,24 @@ nsPrefetchService::PrefetchURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aExpli
if (currentURI) {
PRBool equals;
if (NS_SUCCEEDED(currentURI->Equals(aURI, &equals)) && equals) {
LOG(("rejected: URL is already being prefetched\n"));
return NS_ERROR_ABORT;
if (aOffline) {
// We may still need to put it on the queue if the channel
// isn't fetching to the offline cache
nsCOMPtr<nsICachingChannel> cachingChannel =
do_QueryInterface(mCurrentChannel, &rv);
if (NS_SUCCEEDED(rv)) {
PRBool offline;
rv = cachingChannel->GetCacheForOfflineUse(&offline);
if (NS_SUCCEEDED(rv) && offline) {
LOG(("rejected: URL is already being prefetched\n"));
return NS_ERROR_ABORT;
}
}
}
else {
LOG(("rejected: URL is already being prefetched\n"));
return NS_ERROR_ABORT;
}
}
}
}
@@ -555,12 +664,32 @@ nsPrefetchService::PrefetchURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aExpli
for (; node; node = node->mNext) {
PRBool equals;
if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
if (aOffline) {
// make sure the node is placed in the offline cache
node->mOffline = PR_TRUE;
}
LOG(("rejected: URL is already on prefetch queue\n"));
return NS_ERROR_ABORT;
}
}
return EnqueueURI(aURI, aReferrerURI);
return EnqueueURI(aURI, aReferrerURI, aOffline);
}
NS_IMETHODIMP
nsPrefetchService::PrefetchURI(nsIURI *aURI,
nsIURI *aReferrerURI,
PRBool aExplicit)
{
return Prefetch(aURI, aReferrerURI, aExplicit, PR_FALSE);
}
NS_IMETHODIMP
nsPrefetchService::PrefetchURIForOfflineUse(nsIURI *aURI,
nsIURI *aReferrerURI,
PRBool aExplicit)
{
return Prefetch(aURI, aReferrerURI, aExplicit, PR_TRUE);
}
//-----------------------------------------------------------------------------

View File

@@ -78,11 +78,16 @@ public:
private:
~nsPrefetchService();
nsresult Prefetch(nsIURI *aURI,
nsIURI *aReferrerURI,
PRBool aExplicit,
PRBool aOffline);
void AddProgressListener();
void RemoveProgressListener();
nsresult EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI);
nsresult DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI);
void EmptyQueue();
nsresult EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aOffline);
nsresult DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI, PRBool *aOffline);
void EmptyQueue(PRBool includeOffline);
void StartPrefetching();
void StopPrefetching();
@@ -127,15 +132,18 @@ class nsPrefetchNode
{
public:
nsPrefetchNode(nsIURI *aURI,
nsIURI *aReferrerURI)
nsIURI *aReferrerURI,
PRBool aOffline)
: mNext(nsnull)
, mURI(aURI)
, mReferrerURI(aReferrerURI)
, mOffline(aOffline)
{ }
nsPrefetchNode *mNext;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mReferrerURI;
PRBool mOffline;
};
#endif // !nsPrefetchService_h__