Bug 753990 - Allow appcache to work with a custom cache (profile) folder within a single application, r=michal.novotny

This commit is contained in:
Honza Bambas 2012-06-04 16:12:24 +02:00
Родитель 1a04d1d820
Коммит f4f528d257
26 изменённых файлов: 482 добавлений и 44 удалений

Просмотреть файл

@ -7,6 +7,7 @@
#include "nsISupports.idl"
interface nsIArray;
interface nsILocalFile;
/**
* Application caches can store a set of namespace entries that affect
@ -78,7 +79,7 @@ interface nsIApplicationCacheNamespace : nsISupports
* loads. Inactive caches will be removed from the cache when they are
* no longer referenced.
*/
[scriptable, uuid(32f83e3f-470c-4423-a86a-d35d1c215ccb)]
[scriptable, uuid(231e1e53-05c1-41b6-b9de-dbbcce9385c9)]
interface nsIApplicationCache : nsISupports
{
/**
@ -189,4 +190,10 @@ interface nsIApplicationCache : nsISupports
* Get the most specific namespace matching a given key.
*/
nsIApplicationCacheNamespace getMatchingNamespace(in ACString key);
/**
* If set, this offline cache is placed in a different directory
* than the current application profile.
*/
readonly attribute nsILocalFile cacheDirectory;
};

Просмотреть файл

@ -7,12 +7,13 @@
#include "nsISupports.idl"
interface nsIApplicationCache;
interface nsILocalFile;
/**
* The application cache service manages the set of application cache
* groups.
*/
[scriptable, uuid(10fdea21-1224-4c29-8507-8f3205a121d5)]
[scriptable, uuid(28adfdc7-6718-4b3e-bdb2-ecfefa3c8910)]
interface nsIApplicationCacheService : nsISupports
{
/**
@ -21,6 +22,22 @@ interface nsIApplicationCacheService : nsISupports
*/
nsIApplicationCache createApplicationCache(in ACString group);
/**
* Create a new, empty application cache for the given cache
* group residing in a custom directory with a custom quota.
*
* @param group
* URL of the manifest
* @param directory
* Actually a reference to a profile directory where to
* create the OfflineCache sub-dir.
* @param quota
* Optional override of the default quota.
*/
nsIApplicationCache createCustomApplicationCache(in ACString group,
in nsILocalFile profileDir,
in PRInt32 quota);
/**
* Get an application cache object for the given client ID.
*/

Просмотреть файл

@ -6,6 +6,7 @@
#include "nsICacheInfoChannel.idl"
interface nsIFile;
interface nsILocalFile;
/**
* A channel may optionally implement this interface to allow clients
@ -17,7 +18,7 @@ interface nsIFile;
* 3) Support for uniquely identifying cached data in cases when the URL
* is insufficient (e.g., HTTP form submission).
*/
[scriptable, uuid(830D4BCB-3E46-4011-9BDA-51A5D1AF891F)]
[scriptable, uuid(E2143B61-62FE-4da5-BE2E-E31981095889)]
interface nsICachingChannel : nsICacheInfoChannel
{
/**
@ -87,6 +88,12 @@ interface nsICachingChannel : nsICacheInfoChannel
*/
attribute ACString offlineCacheClientID;
/**
* Override base (profile) directory to work with when accessing the cache.
* When not specified, the current process' profile directory will be used.
*/
attribute nsILocalFile profileDirectory;
/**
* 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

17
netwerk/cache/nsApplicationCacheService.cpp поставляемый
Просмотреть файл

@ -34,6 +34,23 @@ nsApplicationCacheService::CreateApplicationCache(const nsACString &group,
return device->CreateApplicationCache(group, out);
}
NS_IMETHODIMP
nsApplicationCacheService::CreateCustomApplicationCache(const nsACString & group,
nsILocalFile *profileDir,
PRInt32 quota,
nsIApplicationCache **out)
{
if (!mCacheService)
return NS_ERROR_UNEXPECTED;
nsRefPtr<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)

1
netwerk/cache/nsCacheEntry.cpp поставляемый
Просмотреть файл

@ -32,6 +32,7 @@ nsCacheEntry::nsCacheEntry(nsCString * key,
mPredictedDataSize(-1),
mDataSize(0),
mCacheDevice(nsnull),
mCustomDevice(nsnull),
mData(nsnull)
{
MOZ_COUNT_CTOR(nsCacheEntry);

4
netwerk/cache/nsCacheEntry.h поставляемый
Просмотреть файл

@ -63,6 +63,9 @@ public:
nsCacheDevice * CacheDevice() { return mCacheDevice; }
void SetCacheDevice( nsCacheDevice * device) { mCacheDevice = device; }
void SetCustomCacheDevice( nsCacheDevice * device )
{ mCustomDevice = device; }
nsCacheDevice * CustomCacheDevice() { return mCustomDevice; }
const char * GetDeviceID();
/**
@ -216,6 +219,7 @@ private:
PRInt64 mPredictedDataSize; // Size given by ContentLength.
PRUint32 mDataSize; // 4
nsCacheDevice * mCacheDevice; // 4
nsCacheDevice * mCustomDevice; // 4
nsCOMPtr<nsISupports> mSecurityInfo; //
nsISupports * mData; // strong ref
nsCOMPtr<nsIThread> mThread;

4
netwerk/cache/nsCacheRequest.h поставляемый
Просмотреть файл

@ -37,7 +37,8 @@ private:
mInfo(0),
mListener(listener),
mLock("nsCacheRequest.mLock"),
mCondVar(mLock, "nsCacheRequest.mCondVar")
mCondVar(mLock, "nsCacheRequest.mCondVar"),
mProfileDir(session->ProfileDir())
{
MOZ_COUNT_CTOR(nsCacheRequest);
PR_INIT_CLIST(this);
@ -152,6 +153,7 @@ private:
nsCOMPtr<nsIThread> mThread;
Mutex mLock;
CondVar mCondVar;
nsCOMPtr<nsILocalFile> mProfileDir;
};
#endif // _nsCacheRequest_h_

108
netwerk/cache/nsCacheService.cpp поставляемый
Просмотреть файл

@ -1089,6 +1089,7 @@ nsCacheService::nsCacheService()
// create list of cache devices
PR_INIT_CLIST(&mDoomedEntries);
mCustomOfflineDevices.Init();
}
nsCacheService::~nsCacheService()
@ -1143,6 +1144,15 @@ nsCacheService::Init()
return NS_OK;
}
// static
PLDHashOperator
nsCacheService::ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir,
nsRefPtr<nsOfflineCacheDevice>& aDevice,
void* aUserArg)
{
aDevice->Shutdown();
return PL_DHASH_REMOVE;
}
void
nsCacheService::Shutdown()
@ -1198,6 +1208,8 @@ nsCacheService::Shutdown()
NS_IF_RELEASE(mOfflineDevice);
mCustomOfflineDevices.Enumerate(&nsCacheService::ShutdownCustomCacheDeviceEnum, nsnull);
#ifdef PR_LOGGING
LogCacheStatistics();
#endif
@ -1532,32 +1544,75 @@ nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice)
return NS_OK;
}
nsresult
nsCacheService::GetCustomOfflineDevice(nsILocalFile *aProfileDir,
PRInt32 aQuota,
nsOfflineCacheDevice **aDevice)
{
nsresult rv;
nsAutoString profilePath;
rv = aProfileDir->GetPath(profilePath);
NS_ENSURE_SUCCESS(rv, rv);
if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
NS_ENSURE_SUCCESS(rv, rv);
mCustomOfflineDevices.Put(profilePath, *aDevice);
}
return NS_OK;
}
nsresult
nsCacheService::CreateOfflineDevice()
{
CACHE_LOG_ALWAYS(("Creating offline device"));
CACHE_LOG_ALWAYS(("Creating default offline device"));
if (mOfflineDevice) return NS_OK;
if (!mObserver) return NS_ERROR_NOT_AVAILABLE;
nsresult rv = CreateCustomOfflineDevice(
mObserver->OfflineCacheParentDirectory(),
mObserver->OfflineCacheCapacity(),
&mOfflineDevice);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsCacheService::CreateCustomOfflineDevice(nsILocalFile *aProfileDir,
PRInt32 aQuota,
nsOfflineCacheDevice **aDevice)
{
NS_ENSURE_ARG(aProfileDir);
#if defined(PR_LOGGING)
nsCAutoString profilePath;
aProfileDir->GetNativePath(profilePath);
CACHE_LOG_ALWAYS(("Creating custom offline device, %s, %d",
profilePath.BeginReading(), aQuota));
#endif
if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
if (mOfflineDevice) return NS_OK;
mOfflineDevice = new nsOfflineCacheDevice;
if (!mOfflineDevice) return NS_ERROR_OUT_OF_MEMORY;
*aDevice = new nsOfflineCacheDevice;
NS_ADDREF(mOfflineDevice);
NS_ADDREF(*aDevice);
// set the preferences
mOfflineDevice->SetCacheParentDirectory(
mObserver->OfflineCacheParentDirectory());
mOfflineDevice->SetCapacity(mObserver->OfflineCacheCapacity());
(*aDevice)->SetCacheParentDirectory(aProfileDir);
(*aDevice)->SetCapacity(aQuota);
nsresult rv = mOfflineDevice->Init();
nsresult rv = (*aDevice)->Init();
if (NS_FAILED(rv)) {
CACHE_LOG_DEBUG(("mOfflineDevice->Init() failed (0x%.8x)\n", rv));
CACHE_LOG_DEBUG(("OfflineDevice->Init() failed (0x%.8x)\n", rv));
CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
mEnableOfflineDevice = false;
NS_RELEASE(mOfflineDevice);
NS_RELEASE(*aDevice);
}
return rv;
}
@ -1734,6 +1789,20 @@ nsCacheService::ProcessRequest(nsCacheRequest * request,
// loop back around to look for another entry
}
if (NS_SUCCEEDED(rv) && request->mProfileDir) {
// Custom cache directory has been demanded. Preset the cache device.
if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) {
// Failsafe check: this is implemented only for offline cache atm.
rv = NS_ERROR_FAILURE;
} else {
nsRefPtr<nsOfflineCacheDevice> customCacheDevice;
rv = GetCustomOfflineDevice(request->mProfileDir, -1,
getter_AddRefs(customCacheDevice));
if (NS_SUCCEEDED(rv))
entry->SetCustomCacheDevice(customCacheDevice);
}
}
nsICacheEntryDescriptor *descriptor = nsnull;
if (NS_SUCCEEDED(rv))
@ -2046,12 +2115,16 @@ nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
(void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
}
if (mOfflineDevice) {
device = entry->CustomCacheDevice()
? entry->CustomCacheDevice()
: mOfflineDevice;
if (device) {
entry->MarkBinding();
nsresult rv = mOfflineDevice->BindEntry(entry);
nsresult rv = device->BindEntry(entry);
entry->ClearBinding();
if (NS_SUCCEEDED(rv))
device = mOfflineDevice;
if (NS_FAILED(rv))
device = nsnull;
}
}
@ -2146,6 +2219,9 @@ nsCacheService::OnProfileShutdown(bool cleanse)
gService->mOfflineDevice->Shutdown();
}
gService->mCustomOfflineDevices.Enumerate(
&nsCacheService::ShutdownCustomCacheDeviceEnum, nsnull);
gService->mEnableOfflineDevice = false;
if (gService->mMemoryDevice) {

20
netwerk/cache/nsCacheService.h поставляемый
Просмотреть файл

@ -17,6 +17,7 @@
#include "nsIObserver.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
@ -112,6 +113,15 @@ public:
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(nsILocalFile *aProfileDir,
PRInt32 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
@ -183,6 +193,9 @@ private:
nsresult CreateDiskDevice();
nsresult CreateOfflineDevice();
nsresult CreateCustomOfflineDevice(nsILocalFile *aProfileDir,
PRInt32 aQuota,
nsOfflineCacheDevice **aDevice);
nsresult CreateMemoryDevice();
nsresult CreateRequest(nsCacheSession * session,
@ -237,6 +250,11 @@ private:
PLDHashEntryHdr * hdr,
PRUint32 number,
void * arg);
static
PLDHashOperator ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir,
nsRefPtr<nsOfflineCacheDevice>& aDevice,
void* aUserArg);
#if defined(PR_LOGGING)
void LogCacheStatistics();
#endif
@ -270,6 +288,8 @@ private:
nsDiskCacheDevice * mDiskDevice;
nsOfflineCacheDevice * mOfflineDevice;
nsRefPtrHashtable<nsStringHashKey, nsOfflineCacheDevice> mCustomOfflineDevices;
nsCacheEntryHashTable mActiveEntries;
PRCList mDoomedEntries;

25
netwerk/cache/nsCacheSession.cpp поставляемый
Просмотреть файл

@ -41,6 +41,31 @@ NS_IMETHODIMP nsCacheSession::GetDoomEntriesIfExpired(bool *result)
}
NS_IMETHODIMP nsCacheSession::SetProfileDirectory(nsILocalFile *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(nsILocalFile **profileDir)
{
if (mProfileDir)
NS_ADDREF(*profileDir = mProfileDir);
else
*profileDir = nsnull;
return NS_OK;
}
NS_IMETHODIMP nsCacheSession::SetDoomEntriesIfExpired(bool doomEntriesIfExpired)
{
if (doomEntriesIfExpired) MarkDoomEntriesIfExpired();

5
netwerk/cache/nsCacheSession.h поставляемый
Просмотреть файл

@ -9,7 +9,9 @@
#include "nspr.h"
#include "nsError.h"
#include "nsCOMPtr.h"
#include "nsICacheSession.h"
#include "nsILocalFile.h"
#include "nsString.h"
class nsCacheSession : public nsICacheSession
@ -53,9 +55,12 @@ public:
mInfo |= policy;
}
nsILocalFile* ProfileDir() { return mProfileDir; }
private:
nsCString mClientID;
PRUint32 mInfo;
nsCOMPtr<nsILocalFile> mProfileDir;
};
#endif // _nsCacheSession_h_

13
netwerk/cache/nsDiskCacheDeviceSQL.cpp поставляемый
Просмотреть файл

@ -629,6 +629,17 @@ nsApplicationCache::GetClientID(nsACString &out)
return NS_OK;
}
NS_IMETHODIMP
nsApplicationCache::GetCacheDirectory(nsILocalFile **out)
{
if (mDevice->BaseDirectory())
NS_ADDREF(*out = mDevice->BaseDirectory());
else
*out = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsApplicationCache::GetActive(bool *out)
{
@ -2361,6 +2372,8 @@ nsOfflineCacheDevice::SetCacheParentDirectory(nsILocalFile *parentDir)
return;
}
mBaseDirectory = parentDir;
// cache dir may not exist, but that's ok
nsCOMPtr<nsIFile> dir;
rv = parentDir->Clone(getter_AddRefs(dir));

2
netwerk/cache/nsDiskCacheDeviceSQL.h поставляемый
Просмотреть файл

@ -168,6 +168,7 @@ public:
void SetCacheParentDirectory(nsILocalFile * parentDir);
void SetCapacity(PRUint32 capacity);
nsILocalFile * BaseDirectory() { return mBaseDirectory; }
nsILocalFile * CacheDirectory() { return mCacheDirectory; }
PRUint32 CacheCapacity() { return mCacheCapacity; }
PRUint32 CacheSize();
@ -252,6 +253,7 @@ private:
nsCOMPtr<mozIStorageStatement> mStatement_EnumerateGroups;
nsCOMPtr<mozIStorageStatement> mStatement_EnumerateGroupsTimeOrder;
nsCOMPtr<nsILocalFile> mBaseDirectory;
nsCOMPtr<nsILocalFile> mCacheDirectory;
PRUint32 mCacheCapacity; // in bytes
PRInt32 mDeltaCounter;

9
netwerk/cache/nsICacheSession.idl поставляемый
Просмотреть файл

@ -9,6 +9,7 @@
interface nsICacheEntryDescriptor;
interface nsICacheListener;
interface nsILocalFile;
[scriptable, uuid(1dd7708c-de48-4ffe-b5aa-cd218c762887)]
interface nsICacheSession : nsISupports
@ -21,6 +22,14 @@ interface nsICacheSession : nsISupports
*/
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 nsILocalFile 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

Просмотреть файл

@ -2617,6 +2617,11 @@ nsHttpChannel::OpenOfflineCacheEntryForWriting()
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
if (mProfileDirectory) {
rv = session->SetProfileDirectory(mProfileDirectory);
if (NS_FAILED(rv)) return rv;
}
mOnCacheEntryAvailableCallback =
&nsHttpChannel::OnOfflineCacheEntryForWritingAvailable;
rv = session->AsyncOpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
@ -3934,6 +3939,7 @@ nsHttpChannel::SetupReplacementChannel(nsIURI *newURI,
// cacheClientID, cacheForOfflineUse
cachingChannel->SetOfflineCacheClientID(mOfflineCacheClientID);
cachingChannel->SetCacheForOfflineUse(mCacheForOfflineUse);
cachingChannel->SetProfileDirectory(mProfileDirectory);
}
}
@ -5361,6 +5367,22 @@ nsHttpChannel::SetOfflineCacheClientID(const nsACString &value)
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetProfileDirectory(nsILocalFile **_result)
{
NS_ENSURE_ARG(_result);
NS_ADDREF(*_result = mProfileDirectory);
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetProfileDirectory(nsILocalFile *value)
{
mProfileDirectory = value;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetCacheFile(nsIFile **cacheFile)
{

Просмотреть файл

@ -27,6 +27,7 @@
#include "nsIHttpChannelAuthProvider.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsITimedChannel.h"
#include "nsILocalFile.h"
#include "nsDNSPrefetch.h"
#include "TimingStruct.h"
#include "AutoClose.h"
@ -304,6 +305,8 @@ private:
nsCacheAccessMode mOfflineCacheAccess;
nsCString mOfflineCacheClientID;
nsCOMPtr<nsILocalFile> mProfileDirectory;
// auth specific data
nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;

Просмотреть файл

@ -0,0 +1,125 @@
/* 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/. */
/**
* This test executes nsIOfflineCacheUpdateService.scheduleCustomProfileUpdate API
* 1. preloads an app with a manifest to a custom sudir in the profile (for simplicity)
* 2. observes progress and completion of the update
* 3. checks presence of index.sql and files in the expected location
*/
do_load_httpd_js();
var httpServer = null;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /redirect path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// finally check we got fallback content
function finish_test(customDir)
{
var offlineCacheDir = customDir.clone();
offlineCacheDir.append("OfflineCache");
var indexSqlFile = offlineCacheDir.clone();
indexSqlFile.append('index.sqlite');
do_check_eq(indexSqlFile.exists(), true);
var file1 = offlineCacheDir.clone();
file1.append("2");
file1.append("E");
file1.append("2C99DE6E7289A5-0");
do_check_eq(file1.exists(), true);
var file2 = offlineCacheDir.clone();
file2.append("8");
file2.append("6");
file2.append("0B457F75198B29-0");
do_check_eq(file2.exists(), true);
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.start(4444);
var profileDir = do_get_profile();
var customDir = profileDir.clone();
customDir.append("customOfflineCacheDir" + Math.random());
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
ps.setBoolPref("browser.cache.offline.enable", true);
// Set this pref to mimic the default browser behavior.
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, profileDir);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
var update = us.scheduleCustomProfileUpdate(
make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"),
customDir);
var expectedStates = [
Ci.nsIOfflineCacheUpdateObserver.STATE_DOWNLOADING,
Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED,
Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMPROGRESS,
Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMCOMPLETED,
Ci.nsIOfflineCacheUpdateObserver.STATE_FINISHED
];
update.addObserver({
updateStateChanged: function(update, state)
{
do_check_eq(state, expectedStates.shift());
if (state == Ci.nsIOfflineCacheUpdateObserver.STATE_FINISHED)
finish_test(customDir);
},
applicationCacheAvailable: function(appCache)
{
}
}, false);
do_test_pending();
}

Просмотреть файл

@ -187,3 +187,4 @@ run-if = hasNode
[test_xmlhttprequest.js]
[test_XHR_redirects.js]
[test_pinned_app_cache.js]
[test_offlinecache_custom-directory.js]

Просмотреть файл

@ -177,8 +177,9 @@ OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument,
NS_IMETHODIMP
OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
nsIURI *aDocumentURI,
nsIDOMDocument *aDocument)
nsIURI *aDocumentURI,
nsIDOMDocument *aDocument,
nsILocalFile *aCustomProfileDir)
{
nsresult rv;
@ -188,6 +189,11 @@ OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
if (!service)
return NS_ERROR_FAILURE;
if (aCustomProfileDir) {
NS_ERROR("Custom Offline Cache Update not supported on child process");
return NS_ERROR_NOT_IMPLEMENTED;
}
LOG(("OfflineCacheUpdateChild::Init [%p]", this));
// Only http and https applications are supported.

Просмотреть файл

@ -91,7 +91,8 @@ OfflineCacheUpdateGlue::Schedule()
NS_IMETHODIMP
OfflineCacheUpdateGlue::Init(nsIURI *aManifestURI,
nsIURI *aDocumentURI,
nsIDOMDocument *aDocument)
nsIDOMDocument *aDocument,
nsILocalFile *aCustomProfileDir)
{
if (!EnsureUpdate())
return NS_ERROR_NULL_POINTER;
@ -101,7 +102,7 @@ OfflineCacheUpdateGlue::Init(nsIURI *aManifestURI,
if (aDocument)
SetDocument(aDocument);
return mUpdate->Init(aManifestURI, aDocumentURI, nsnull);
return mUpdate->Init(aManifestURI, aDocumentURI, nsnull, aCustomProfileDir);
}
void

Просмотреть файл

@ -35,8 +35,8 @@ namespace docshell {
NS_SCRIPTABLE NS_IMETHOD GetByteProgress(PRUint64 * _result) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetByteProgress(_result); }
class OfflineCacheUpdateGlue : public nsSupportsWeakReference
, public nsIOfflineCacheUpdate
, public nsIOfflineCacheUpdateObserver
, public nsIOfflineCacheUpdate
, public nsIOfflineCacheUpdateObserver
{
public:
NS_DECL_ISUPPORTS
@ -49,7 +49,8 @@ public:
NS_SCRIPTABLE NS_IMETHOD Schedule(void);
NS_SCRIPTABLE NS_IMETHOD Init(nsIURI *aManifestURI,
nsIURI *aDocumentURI,
nsIDOMDocument *aDocument);
nsIDOMDocument *aDocument,
nsILocalFile *aCustomProfileDir);
NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER

Просмотреть файл

@ -83,7 +83,7 @@ OfflineCacheUpdateParent::Schedule(const URI& aManifestURI,
nsresult rv;
// Leave aDocument argument null. Only glues and children keep
// document instances.
rv = update->Init(manifestURI, documentURI, nsnull);
rv = update->Init(manifestURI, documentURI, nsnull, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
rv = update->Schedule();

Просмотреть файл

@ -14,6 +14,7 @@ interface nsIOfflineCacheUpdate;
interface nsIPrincipal;
interface nsIPrefBranch;
interface nsIApplicationCache;
interface nsILocalFile;
[scriptable, uuid(47360d57-8ef4-4a5d-8865-1a27a739ad1a)]
interface nsIOfflineCacheUpdateObserver : nsISupports {
@ -105,7 +106,8 @@ interface nsIOfflineCacheUpdate : nsISupports {
* @param aDocumentURI
* The page that is requesting the update.
*/
void init(in nsIURI aManifestURI, in nsIURI aDocumentURI, in nsIDOMDocument aDocument);
void init(in nsIURI aManifestURI, in nsIURI aDocumentURI, in nsIDOMDocument aDocument,
[optional] in nsILocalFile aCustomProfileDir);
/**
* Initialize the update for partial processing.
@ -162,7 +164,7 @@ interface nsIOfflineCacheUpdate : nsISupports {
readonly attribute PRUint64 byteProgress;
};
[scriptable, uuid(6fd2030f-7b00-4102-a0e3-d73078821eb1)]
[scriptable, uuid(dc5de18c-197c-41d2-9584-dd7ac7494611)]
interface nsIOfflineCacheUpdateService : nsISupports {
/**
* Constants for the offline-app permission.
@ -192,6 +194,15 @@ interface nsIOfflineCacheUpdateService : nsISupports {
in nsIURI aDocumentURI,
in nsIDOMWindow aWindow);
/**
* Schedule a cache update for a given offline manifest and let the data
* be stored to a custom profile directory. There is no coalescing of
* manifests by manifest URL.
*/
nsIOfflineCacheUpdate scheduleCustomProfileUpdate(in nsIURI aManifestURI,
in nsIURI aDocumentURI,
in nsILocalFile aProfileDir);
/**
* Schedule a cache update for a manifest when the document finishes
* loading.

Просмотреть файл

@ -48,6 +48,9 @@ static const PRUint32 kPinnedEntryRetriesLimit = 3;
// Maximum number of parallel items loads
static const PRUint32 kParallelLoadLimit = 15;
// Quota for offline apps when preloading
static const PRInt32 kCustomProfileQuota = 512000;
#if defined(PR_LOGGING)
//
// To enable logging (see prlog.h for full details):
@ -284,11 +287,13 @@ NS_IMPL_ISUPPORTS6(nsOfflineCacheUpdateItem,
nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsIURI *aURI,
nsIURI *aReferrerURI,
nsIApplicationCache *aApplicationCache,
nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID,
PRUint32 type)
: mURI(aURI)
, mReferrerURI(aReferrerURI)
, mApplicationCache(aApplicationCache)
, mPreviousApplicationCache(aPreviousApplicationCache)
, mClientID(aClientID)
, mItemType(type)
@ -350,6 +355,13 @@ nsOfflineCacheUpdateItem::OpenChannel(nsOfflineCacheUpdate *aUpdate)
rv = cachingChannel->SetCacheForOfflineUse(true);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILocalFile> cacheDirectory;
rv = mApplicationCache->GetCacheDirectory(getter_AddRefs(cacheDirectory));
NS_ENSURE_SUCCESS(rv, rv);
rv = cachingChannel->SetProfileDirectory(cacheDirectory);
NS_ENSURE_SUCCESS(rv, rv);
if (!mClientID.IsEmpty()) {
rv = cachingChannel->SetOfflineCacheClientID(mClientID);
NS_ENSURE_SUCCESS(rv, rv);
@ -497,10 +509,18 @@ nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
if (newCachingChannel) {
rv = newCachingChannel->SetCacheForOfflineUse(true);
NS_ENSURE_SUCCESS(rv, rv);
if (!mClientID.IsEmpty()) {
rv = newCachingChannel->SetOfflineCacheClientID(mClientID);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsILocalFile> cacheDirectory;
rv = mApplicationCache->GetCacheDirectory(getter_AddRefs(cacheDirectory));
NS_ENSURE_SUCCESS(rv, rv);
rv = newCachingChannel->SetProfileDirectory(cacheDirectory);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCAutoString oldScheme;
@ -662,10 +682,11 @@ nsOfflineCacheUpdateItem::GetStatus(PRUint16 *aStatus)
nsOfflineManifestItem::nsOfflineManifestItem(nsIURI *aURI,
nsIURI *aReferrerURI,
nsIApplicationCache *aApplicationCache,
nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID)
: nsOfflineCacheUpdateItem(aURI, aReferrerURI,
aPreviousApplicationCache, aClientID,
aApplicationCache, aPreviousApplicationCache, aClientID,
nsIApplicationCache::ITEM_MANIFEST)
, mParserState(PARSE_INIT)
, mNeedsUpdate(true)
@ -1172,7 +1193,8 @@ nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey)
nsresult
nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
nsIURI *aDocumentURI,
nsIDOMDocument *aDocument)
nsIDOMDocument *aDocument,
nsILocalFile *aCustomProfileDir)
{
nsresult rv;
@ -1214,13 +1236,32 @@ nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = cacheService->GetActiveCache(manifestSpec,
getter_AddRefs(mPreviousApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
if (aCustomProfileDir) {
// Create only a new offline application cache in the custom profile
// This is a preload of a new cache.
rv = cacheService->CreateApplicationCache(manifestSpec,
getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
// XXX Custom updates don't support "updating" of an existing cache
// in the custom profile at the moment. This support can be, though,
// simply added as well when needed.
mPreviousApplicationCache = nsnull;
rv = cacheService->CreateCustomApplicationCache(manifestSpec,
aCustomProfileDir,
kCustomProfileQuota,
getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
mCustomProfileDir = aCustomProfileDir;
}
else {
rv = cacheService->GetActiveCache(manifestSpec,
getter_AddRefs(mPreviousApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
rv = cacheService->CreateApplicationCache(manifestSpec,
getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
}
rv = mApplicationCache->GetClientID(mClientID);
NS_ENSURE_SUCCESS(rv, rv);
@ -1547,7 +1588,7 @@ nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
new nsOfflineCacheUpdate();
// Leave aDocument argument null. Only glues and children keep
// document instances.
newUpdate->Init(mManifestURI, mDocumentURI, nsnull);
newUpdate->Init(mManifestURI, mDocumentURI, nsnull, mCustomProfileDir);
// In a rare case the manifest will not be modified on the next refetch
// transfer all master document URIs to the new update to ensure that
@ -1587,6 +1628,7 @@ nsOfflineCacheUpdate::Begin()
mManifestItem = new nsOfflineManifestItem(mManifestURI,
mDocumentURI,
mApplicationCache,
mPreviousApplicationCache,
mClientID);
if (!mManifestItem) {
@ -2091,8 +2133,11 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aURI, PRUint32 aType)
}
nsRefPtr<nsOfflineCacheUpdateItem> item =
new nsOfflineCacheUpdateItem(aURI, mDocumentURI,
mPreviousApplicationCache, mClientID,
new nsOfflineCacheUpdateItem(aURI,
mDocumentURI,
mApplicationCache,
mPreviousApplicationCache,
mClientID,
aType);
if (!item) return NS_ERROR_OUT_OF_MEMORY;

Просмотреть файл

@ -54,6 +54,7 @@ public:
nsOfflineCacheUpdateItem(nsIURI *aURI,
nsIURI *aReferrerURI,
nsIApplicationCache *aApplicationCache,
nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID,
PRUint32 aType);
@ -61,6 +62,7 @@ public:
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mReferrerURI;
nsCOMPtr<nsIApplicationCache> mApplicationCache;
nsCOMPtr<nsIApplicationCache> mPreviousApplicationCache;
nsCString mClientID;
nsCString mCacheKey;
@ -92,6 +94,7 @@ public:
nsOfflineManifestItem(nsIURI *aURI,
nsIURI *aReferrerURI,
nsIApplicationCache *aApplicationCache,
nsIApplicationCache *aPreviousApplicationCache,
const nsACString &aClientID);
virtual ~nsOfflineManifestItem();
@ -254,6 +257,7 @@ private:
nsCString mUpdateDomain;
nsCOMPtr<nsIURI> mManifestURI;
nsCOMPtr<nsIURI> mDocumentURI;
nsCOMPtr<nsILocalFile> mCustomProfileDir;
nsCString mClientID;
nsCOMPtr<nsIApplicationCache> mApplicationCache;
@ -313,6 +317,7 @@ public:
nsIURI *aDocumentURI,
nsIDOMDocument *aDocument,
nsIDOMWindow* aWindow,
nsILocalFile* aCustomProfileDir,
nsIOfflineCacheUpdate **aUpdate);
virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);

Просмотреть файл

@ -161,7 +161,7 @@ nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress,
if (NS_SUCCEEDED(aStatus)) {
nsCOMPtr<nsIOfflineCacheUpdate> update;
mService->Schedule(mManifestURI, mDocumentURI,
updateDoc, window, getter_AddRefs(update));
updateDoc, window, nsnull, getter_AddRefs(update));
}
aWebProgress->RemoveProgressListener(this);
@ -436,6 +436,7 @@ nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
nsIURI *aDocumentURI,
nsIDOMDocument *aDocument,
nsIDOMWindow* aWindow,
nsILocalFile* aCustomProfileDir,
nsIOfflineCacheUpdate **aUpdate)
{
nsCOMPtr<nsIOfflineCacheUpdate> update;
@ -448,7 +449,7 @@ nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
nsresult rv;
rv = update->Init(aManifestURI, aDocumentURI, aDocument);
rv = update->Init(aManifestURI, aDocumentURI, aDocument, aCustomProfileDir);
NS_ENSURE_SUCCESS(rv, rv);
rv = update->Schedule();
@ -465,7 +466,19 @@ nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
nsIDOMWindow *aWindow,
nsIOfflineCacheUpdate **aUpdate)
{
return Schedule(aManifestURI, aDocumentURI, nsnull, aWindow, aUpdate);
return Schedule(aManifestURI, aDocumentURI, nsnull, aWindow, nsnull, aUpdate);
}
NS_IMETHODIMP
nsOfflineCacheUpdateService::ScheduleCustomProfileUpdate(nsIURI *aManifestURI,
nsIURI *aDocumentURI,
nsILocalFile *aProfileDir,
nsIOfflineCacheUpdate **aUpdate)
{
// The profile directory is mandatory
NS_ENSURE_ARG(aProfileDir);
return Schedule(aManifestURI, aDocumentURI, nsnull, nsnull, aProfileDir, aUpdate);
}
//-----------------------------------------------------------------------------