зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1170837 - Provide way to update packaged apps r=honzab
This commit is contained in:
Родитель
ca03b1a8ed
Коммит
31d2ce9ce4
|
@ -14,6 +14,7 @@
|
|||
#include "../../cache2/CacheFileUtils.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
@ -49,6 +50,8 @@ LogURI(const char *aFunctionName, void *self, nsIURI *aURI, nsILoadContextInfo *
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* static */ nsresult
|
||||
PackagedAppService::CacheEntryWriter::Create(nsIURI *aURI,
|
||||
nsICacheStorage *aStorage,
|
||||
|
@ -201,6 +204,48 @@ PackagedAppService::CacheEntryWriter::OnDataAvailable(nsIRequest *aRequest,
|
|||
return aInputStream->ReadSegments(ConsumeData, this, aCount, &n);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_IMPL_ISUPPORTS(PackagedAppService::PackagedAppChannelListener, nsIStreamListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
PackagedAppService::PackagedAppChannelListener::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
bool isFromCache = false;
|
||||
nsCOMPtr<nsICacheInfoChannel> cacheChan = do_QueryInterface(aRequest);
|
||||
if (cacheChan) {
|
||||
cacheChan->IsFromCache(&isFromCache);
|
||||
}
|
||||
|
||||
mDownloader->SetIsFromCache(isFromCache);
|
||||
LOG(("[%p] Downloader isFromCache: %d\n", mDownloader.get(), isFromCache));
|
||||
|
||||
// XXX: This is the place to suspend the channel, doom existing cache entries
|
||||
// for previous resources, and then resume the channel.
|
||||
return mListener->OnStartRequest(aRequest, aContext);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PackagedAppService::PackagedAppChannelListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
return mListener->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PackagedAppService::PackagedAppChannelListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsIInputStream *aInputStream,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
return mListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_IMPL_ISUPPORTS(PackagedAppService::PackagedAppDownloader, nsIStreamListener)
|
||||
|
||||
|
@ -355,7 +400,10 @@ PackagedAppService::PackagedAppDownloader::OnStopRequest(nsIRequest *aRequest,
|
|||
|
||||
// If this is the last part of the package, it means the requested resources
|
||||
// have not been found in the package so we return an appropriate error.
|
||||
if (NS_SUCCEEDED(aStatusCode) && lastPart) {
|
||||
// If the package response comes from the cache, we want to preserve the
|
||||
// statusCode, so ClearCallbacks looks for the resource in the cache, instead
|
||||
// of returning NS_ERROR_FILE_NOT_FOUND.
|
||||
if (NS_SUCCEEDED(aStatusCode) && lastPart && !mIsFromCache) {
|
||||
aStatusCode = NS_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
@ -397,9 +445,20 @@ PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
|
|||
// Check if we already have a resource waiting for this resource
|
||||
nsCOMArray<nsICacheEntryOpenCallback>* array = mCallbacks.Get(spec);
|
||||
if (array) {
|
||||
// Add this resource to the callback array
|
||||
array->AppendObject(aCallback);
|
||||
if (array->Length() == 0) {
|
||||
// The download of this resource has already completed, hence we don't
|
||||
// need to wait for it to be inserted in the cache and we can serve it
|
||||
// right now, directly. See also the CallCallbacks method bellow.
|
||||
LOG(("[%p] > already downloaded\n", this));
|
||||
mCacheStorage->AsyncOpenURI(aURI, EmptyCString(),
|
||||
nsICacheStorage::OPEN_READONLY, aCallback);
|
||||
} else {
|
||||
LOG(("[%p] > adding to array\n", this));
|
||||
// Add this resource to the callback array
|
||||
array->AppendObject(aCallback);
|
||||
}
|
||||
} else {
|
||||
LOG(("[%p] > creating array\n", this));
|
||||
// This is the first callback for this URI.
|
||||
// Create a new array and add the callback
|
||||
nsCOMArray<nsICacheEntryOpenCallback>* newArray =
|
||||
|
@ -423,7 +482,7 @@ PackagedAppService::PackagedAppDownloader::CallCallbacks(nsIURI *aURI,
|
|||
LOG(("[%p] > status:%X\n", this, aResult));
|
||||
|
||||
nsAutoCString spec;
|
||||
aURI->GetSpec(spec);
|
||||
aURI->GetAsciiSpec(spec);
|
||||
|
||||
nsCOMArray<nsICacheEntryOpenCallback>* array = mCallbacks.Get(spec);
|
||||
if (array) {
|
||||
|
@ -434,27 +493,23 @@ PackagedAppService::PackagedAppDownloader::CallCallbacks(nsIURI *aURI,
|
|||
mCacheStorage->AsyncOpenURI(aURI, EmptyCString(),
|
||||
nsICacheStorage::OPEN_READONLY, callback);
|
||||
}
|
||||
// Clear the array and remove it from the hashtable
|
||||
// Clear the array but leave it in the hashtable
|
||||
// An empty array means that the resource was already downloaded, and a
|
||||
// new call to AddCallback can simply return it from the cache.
|
||||
array->Clear();
|
||||
mCallbacks.Remove(spec);
|
||||
aEntry->ForceValidFor(0);
|
||||
LOG(("[%p] > called callbacks\n", this));
|
||||
} else {
|
||||
// There were no listeners waiting for this resource, but we insert a new
|
||||
// empty array into the hashtable so if any new callbacks are added while
|
||||
// downloading the package, we can simply return it from the cache.
|
||||
nsCOMArray<nsICacheEntryOpenCallback>* newArray =
|
||||
new nsCOMArray<nsICacheEntryOpenCallback>();
|
||||
mCallbacks.Put(spec, newArray);
|
||||
LOG(("[%p] > created array\n", this));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
PackagedAppService::PackagedAppDownloader::ClearCallbacksEnumerator(const nsACString& key,
|
||||
nsAutoPtr<nsCOMArray<nsICacheEntryOpenCallback> >& callbackArray,
|
||||
void* arg)
|
||||
{
|
||||
MOZ_ASSERT(arg, "The void* parameter should be a pointer to nsresult");
|
||||
nsresult *result = static_cast<nsresult*>(arg);
|
||||
for (uint32_t i = 0; i < callbackArray->Length(); ++i) {
|
||||
nsCOMPtr<nsICacheEntryOpenCallback> callback = callbackArray->ObjectAt(i);
|
||||
callback->OnCacheEntryAvailable(nullptr, false, nullptr, *result);
|
||||
}
|
||||
// Remove entry from hashtable
|
||||
return PL_DHASH_REMOVE;
|
||||
aEntry->ForceValidFor(0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -463,48 +518,44 @@ PackagedAppService::PackagedAppDownloader::ClearCallbacks(nsresult aResult)
|
|||
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
|
||||
LOG(("[%p] PackagedAppService::PackagedAppDownloader::ClearCallbacks > packageKey:%s status:%X\n",
|
||||
this, mPackageKey.get(), aResult));
|
||||
mCallbacks.Enumerate(ClearCallbacksEnumerator, &aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(PackagedAppService::CacheEntryChecker, nsICacheEntryOpenCallback)
|
||||
for (auto iter = mCallbacks.Iter(); !iter.Done(); iter.Next()) {
|
||||
const nsACString& key = iter.Key();
|
||||
const nsCOMArray<nsICacheEntryOpenCallback>* callbackArray = iter.UserData();
|
||||
|
||||
NS_IMETHODIMP
|
||||
PackagedAppService::CacheEntryChecker::OnCacheEntryCheck(nsICacheEntry *aEntry,
|
||||
nsIApplicationCache *aApplicationCache,
|
||||
uint32_t *_retval)
|
||||
{
|
||||
nsresult rv = mCallback->OnCacheEntryCheck(aEntry, aApplicationCache, _retval);
|
||||
LOG(("[%p] PackagedAppService::CacheEntryChecker::OnCacheEntryCheck > rv=%X retval=%X\n",
|
||||
this, rv, _retval));
|
||||
return rv;
|
||||
}
|
||||
if (NS_SUCCEEDED(aResult)) {
|
||||
// For success conditions we try to open the cache entry.
|
||||
// This can occur when the package metadata is served from the cache,
|
||||
// as it hasn't changed, but the entries are still in the cache.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
DebugOnly<nsresult> rv = NS_NewURI(getter_AddRefs(uri), key);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
NS_IMETHODIMP
|
||||
PackagedAppService::CacheEntryChecker::OnCacheEntryAvailable(nsICacheEntry *aEntry,
|
||||
bool aNew,
|
||||
nsIApplicationCache *aApplicationCache,
|
||||
nsresult aResult)
|
||||
{
|
||||
LogURI("PackagedAppService::CacheEntryChecker::OnCacheEntryAvailable",
|
||||
this, mURI, mLoadContextInfo);
|
||||
if (aResult == NS_ERROR_CACHE_KEY_NOT_FOUND) {
|
||||
MOZ_ASSERT(!aEntry, "No entry");
|
||||
LOG(("[%p] > NOT FOUND\n", this));
|
||||
// trigger download
|
||||
// download checks if package download is already in progress
|
||||
gPackagedAppService->OpenNewPackageInternal(mURI, mCallback,
|
||||
mLoadContextInfo);
|
||||
} else {
|
||||
LOG(("[%p] > FOUND ENTRY status:%X entry:%p\n", this, aResult, aEntry));
|
||||
// TODO: if aResult is another error code, should we pass it off to the
|
||||
// consumer, or should we try to download the package again?
|
||||
mCallback->OnCacheEntryAvailable(aEntry, aNew, aApplicationCache, aResult);
|
||||
// TODO: update last access entry for the entire package
|
||||
LOG(("[%p] > calling AsyncOpenURI for %s\n", this, key.BeginReading()));
|
||||
for (uint32_t i = 0; i < callbackArray->Length(); ++i) {
|
||||
nsCOMPtr<nsICacheEntryOpenCallback> callback = callbackArray->ObjectAt(i);
|
||||
mCacheStorage->AsyncOpenURI(uri, EmptyCString(),
|
||||
nsICacheStorage::OPEN_READONLY, callback);
|
||||
}
|
||||
|
||||
} else { // an error has occured
|
||||
// We just call all the callbacks and pass the error result
|
||||
LOG(("[%p] > passing NULL cache entry for %s\n", this, key.BeginReading()));
|
||||
for (uint32_t i = 0; i < callbackArray->Length(); ++i) {
|
||||
nsCOMPtr<nsICacheEntryOpenCallback> callback = callbackArray->ObjectAt(i);
|
||||
callback->OnCacheEntryAvailable(nullptr, false, nullptr, aResult);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we remove this entry from the hashtable.
|
||||
iter.Remove();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PackagedAppService::PackagedAppService()
|
||||
{
|
||||
gPackagedAppService = this;
|
||||
|
@ -563,66 +614,14 @@ PackagedAppService::RequestURI(nsIURI *aURI,
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoCString path;
|
||||
aURI->GetPath(path);
|
||||
int32_t pos = path.Find(PACKAGED_APP_TOKEN);
|
||||
if (pos == kNotFound) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
LogURI("PackagedAppService::RequestURI", this, aURI, aInfo);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsICacheStorageService> cacheStorageService =
|
||||
do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
|
||||
LogURI("PackagedAppService::RequestURI", this, aURI, aInfo);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICacheStorage> cacheStorage;
|
||||
|
||||
rv = cacheStorageService->DiskCacheStorage(aInfo, false,
|
||||
getter_AddRefs(cacheStorage));
|
||||
|
||||
nsRefPtr<CacheEntryChecker> checker = new CacheEntryChecker(aURI, aCallback, aInfo);
|
||||
return cacheStorage->AsyncOpenURI(aURI, EmptyCString(),
|
||||
nsICacheStorage::OPEN_READONLY, checker);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PackagedAppService::NotifyPackageDownloaded(nsCString aKey)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
|
||||
mDownloadingPackages.Remove(aKey);
|
||||
LOG(("[%p] PackagedAppService::NotifyPackageDownloaded > %s\n", this, aKey.get()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PackagedAppService::OpenNewPackageInternal(nsIURI *aURI,
|
||||
nsICacheEntryOpenCallback *aCallback,
|
||||
nsILoadContextInfo *aInfo)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
|
||||
|
||||
nsAutoCString path;
|
||||
nsresult rv = aURI->GetPath(path);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
int32_t pos = path.Find(PACKAGED_APP_TOKEN);
|
||||
MOZ_ASSERT(pos != kNotFound,
|
||||
"This should never be called if the token is missing");
|
||||
|
||||
nsCOMPtr<nsIURI> packageURI;
|
||||
rv = aURI->CloneIgnoringRef(getter_AddRefs(packageURI));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = packageURI->SetPath(Substring(path, 0, pos));
|
||||
rv = GetPackageURI(aURI, getter_AddRefs(packageURI));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -688,8 +687,7 @@ PackagedAppService::OpenNewPackageInternal(nsIURI *aURI,
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIStreamListener> mimeConverter;
|
||||
// Passing `this` as context, so nsMultiMixedConv knows this is a packaged app
|
||||
rv = streamconv->AsyncConvertData("multipart/mixed", "*/*", downloader, this,
|
||||
rv = streamconv->AsyncConvertData(APPLICATION_PACKAGE, "*/*", downloader, nullptr,
|
||||
getter_AddRefs(mimeConverter));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
|
@ -698,7 +696,19 @@ PackagedAppService::OpenNewPackageInternal(nsIURI *aURI,
|
|||
// Add the package to the hashtable.
|
||||
mDownloadingPackages.Put(key, downloader);
|
||||
|
||||
return channel->AsyncOpen(mimeConverter, nullptr);
|
||||
nsRefPtr<PackagedAppChannelListener> listener =
|
||||
new PackagedAppChannelListener(downloader, mimeConverter);
|
||||
|
||||
return channel->AsyncOpen(listener, nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PackagedAppService::NotifyPackageDownloaded(nsCString aKey)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
|
||||
mDownloadingPackages.Remove(aKey);
|
||||
LOG(("[%p] PackagedAppService::NotifyPackageDownloaded > %s\n", this, aKey.get()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -27,10 +27,6 @@ class nsHttpResponseHead;
|
|||
// aURI is the subresource uri - http://domain.com/path/package!//resource.html
|
||||
// aInfo is a nsILoadContextInfo used to pick the cache jar the resource goes into
|
||||
// aCallback is the target of the async call to requestURI
|
||||
// When requestURI is called, a CacheEntryChecker is created to verify if the
|
||||
// resource is already in the cache. If it is, it passes it to the callback.
|
||||
// Otherwise, it starts downloading the package. When the packaged resource has
|
||||
// been downloaded, its cache entry gets passed to the callback.
|
||||
class PackagedAppService final
|
||||
: public nsIPackagedAppService
|
||||
{
|
||||
|
@ -42,20 +38,6 @@ class PackagedAppService final
|
|||
private:
|
||||
~PackagedAppService();
|
||||
|
||||
// This method is called if an entry wasn't found in the cache.
|
||||
// It checks to see if the package is currently being downloaded.
|
||||
// If so, then it simply adds the callback to that PackageAppDownloader
|
||||
// Else it begins downloading the new package and adds it to mDownloadingPackages
|
||||
// - aURI is the packaged resource's URL
|
||||
// - aCallback is the listener which gets called when the requested
|
||||
// resource is available.
|
||||
// - aInfo is needed because cache entries are located in separate cache jars
|
||||
// If a resource isn't found in the package, aCallback->OnCacheEntryAvailable
|
||||
// will be called with a null entry and an error result as a status.
|
||||
nsresult OpenNewPackageInternal(nsIURI *aURI,
|
||||
nsICacheEntryOpenCallback *aCallback,
|
||||
nsILoadContextInfo *aInfo);
|
||||
|
||||
// Called by PackageAppDownloader once the download has finished
|
||||
// (or encountered an error) to remove the package from mDownloadingPackages
|
||||
// Should be called on the main thread.
|
||||
|
@ -78,8 +60,7 @@ private:
|
|||
|
||||
// If successful, calling this static method will create a new
|
||||
// CacheEntryWriter and will create the cache entry associated to the
|
||||
// resource and open an output stream which we use for writing the resource's
|
||||
// content into the cache entry.
|
||||
// resource.
|
||||
static nsresult Create(nsIURI*, nsICacheStorage*, CacheEntryWriter**);
|
||||
|
||||
nsCOMPtr<nsICacheEntry> mEntry;
|
||||
|
@ -130,6 +111,10 @@ private:
|
|||
// aURI is the full URI of a subresource, composed of packageURI + !// + subresourcePath
|
||||
nsresult AddCallback(nsIURI *aURI, nsICacheEntryOpenCallback *aCallback);
|
||||
|
||||
// Called by PackagedAppChannelListener to note the fact that the package
|
||||
// is coming from the cache, and no subresources are to be expected as only
|
||||
// package metadata is saved in the cache.
|
||||
void SetIsFromCache(bool aFromCache) { mIsFromCache = aFromCache; }
|
||||
private:
|
||||
~PackagedAppDownloader() { }
|
||||
|
||||
|
@ -142,9 +127,6 @@ private:
|
|||
// cause us to call OnCacheEntryAvailable with a null entry. This would be
|
||||
// equivalent to a 404 when loading from the net.
|
||||
nsresult ClearCallbacks(nsresult aResult);
|
||||
static PLDHashOperator ClearCallbacksEnumerator(const nsACString& key,
|
||||
nsAutoPtr<nsCOMArray<nsICacheEntryOpenCallback>>& callbackArray,
|
||||
void* arg);
|
||||
// Returns a URI with the subresource's full URI
|
||||
// The request must be QIable to nsIResponseHeadProvider since it looks
|
||||
// at the Content-Location header to compute the full path.
|
||||
|
@ -162,33 +144,38 @@ private:
|
|||
// The key with which this package is inserted in
|
||||
// PackagedAppService::mDownloadingPackages
|
||||
nsCString mPackageKey;
|
||||
|
||||
// Whether the package is from the cache
|
||||
bool mIsFromCache;
|
||||
};
|
||||
|
||||
// This class is used to check if a packaged resource has already been
|
||||
// downloaded and saved into the cache.
|
||||
// It calls aCallback->OnCacheEntryAvailable if the resource exists in the
|
||||
// cache or PackagedAppService::OpenNewPackageInternal if it needs
|
||||
// to be downloaded
|
||||
class CacheEntryChecker final
|
||||
: public nsICacheEntryOpenCallback
|
||||
// Intercepts OnStartRequest, OnDataAvailable*, OnStopRequest method calls
|
||||
// and forwards them to the listener.
|
||||
// The target is a `mListener` which converts the package to individual
|
||||
// resources and serves them to mDownloader.
|
||||
// This class is able to perform conditional actions based on whether the
|
||||
// underlying nsIHttpChannel is served from the cache.
|
||||
// As this object serves as a listener for the channel, it is kept alive until
|
||||
// calling OnStopRequest is completed.
|
||||
class PackagedAppChannelListener final
|
||||
: public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICACHEENTRYOPENCALLBACK
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
||||
CacheEntryChecker(nsIURI *aURI, nsICacheEntryOpenCallback * aCallback,
|
||||
nsILoadContextInfo *aInfo)
|
||||
: mURI(aURI)
|
||||
, mCallback(aCallback)
|
||||
, mLoadContextInfo(aInfo)
|
||||
PackagedAppChannelListener(PackagedAppDownloader *aDownloader,
|
||||
nsIStreamListener *aListener)
|
||||
: mDownloader(aDownloader)
|
||||
, mListener(aListener)
|
||||
{
|
||||
}
|
||||
private:
|
||||
~CacheEntryChecker() { }
|
||||
~PackagedAppChannelListener() { }
|
||||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
|
||||
nsCOMPtr<nsILoadContextInfo> mLoadContextInfo;
|
||||
nsRefPtr<PackagedAppDownloader> mDownloader;
|
||||
nsCOMPtr<nsIStreamListener> mListener; // nsMultiMixedConv
|
||||
};
|
||||
|
||||
// A hashtable of packages that are currently being downloaded.
|
||||
|
|
|
@ -43,8 +43,19 @@ var packagedAppRequestsMade = 0;
|
|||
function packagedAppContentHandler(metadata, response)
|
||||
{
|
||||
packagedAppRequestsMade++;
|
||||
if (packagedAppRequestsMade == 2) {
|
||||
// The second request returns a 304 not modified response
|
||||
response.setStatusLine(metadata.httpVersion, 304, "Not Modified");
|
||||
response.bodyOutputStream.write("", 0);
|
||||
return;
|
||||
}
|
||||
response.setHeader("Content-Type", 'application/package');
|
||||
var body = testData.getData();
|
||||
|
||||
if (packagedAppRequestsMade == 3) {
|
||||
// The third request returns a 200 OK response with a slightly different content
|
||||
body = body.replace(/\.\.\./g, 'xxx');
|
||||
}
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
|
@ -103,6 +114,7 @@ function run_test()
|
|||
add_test(test_callback_gets_called);
|
||||
add_test(test_same_content);
|
||||
add_test(test_request_number);
|
||||
add_test(test_updated_package);
|
||||
|
||||
add_test(test_package_does_not_exist);
|
||||
add_test(test_file_does_not_exist);
|
||||
|
@ -127,24 +139,33 @@ var metadataListener = {
|
|||
}
|
||||
|
||||
// A listener we use to check the proper cache entry is returned by the service
|
||||
// NOTE: this listener only checks the content of index.html
|
||||
// Don't use it when requesting other packaged resources! :)
|
||||
var cacheListener = {
|
||||
function packagedResourceListener(content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
packagedResourceListener.prototype = {
|
||||
QueryInterface: function (iid) {
|
||||
if (iid.equals(Ci.nsICacheEntryOpenCallback) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
|
||||
onCacheEntryAvailable: function (entry, isnew, appcache, status) {
|
||||
ok(!!entry, "Needs to have an entry");
|
||||
equal(status, Cr.NS_OK, "status is NS_OK");
|
||||
ok(!!entry, "Needs to have an entry");
|
||||
equal(entry.key, uri + packagePath + "!//index.html", "Check entry has correct name");
|
||||
entry.visitMetaData(metadataListener);
|
||||
var inputStream = entry.openInputStream(0);
|
||||
pumpReadStream(inputStream, function(read) {
|
||||
pumpReadStream(inputStream, (read) => {
|
||||
inputStream.close();
|
||||
equal(read,"<html>\r\n <head>\r\n <script src=\"/scripts/app.js\"></script>\r\n ...\r\n </head>\r\n ...\r\n</html>\r\n"); // not using do_check_eq since logger will fail for the 1/4MB string
|
||||
equal(read, this.content); // not using do_check_eq since logger will fail for the 1/4MB string
|
||||
run_next_test();
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
};
|
||||
|
||||
var cacheListener = new packagedResourceListener(testData.content[0].data);
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// These calls should fail, since one of the arguments is invalid or null
|
||||
|
@ -170,12 +191,19 @@ function test_same_content() {
|
|||
paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
|
||||
}
|
||||
|
||||
// Check the package has only been requested once.
|
||||
// Check the content handler has been called the expected number of times.
|
||||
function test_request_number() {
|
||||
equal(packagedAppRequestsMade, 1, "only one request should be made. Second should be loaded from cache");
|
||||
equal(packagedAppRequestsMade, 2, "2 requests are expected. First with content, second is a 304 not modified.");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// This tests that new content is returned if the package has been updated
|
||||
function test_updated_package() {
|
||||
packagePath = "/package";
|
||||
paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default,
|
||||
new packagedResourceListener(testData.content[0].data.replace(/\.\.\./g, 'xxx')));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// This listener checks that the requested resources are not returned
|
||||
|
|
Загрузка…
Ссылка в новой задаче