Bug 931249 - patch 4 - comparison. r=nsm

--HG--
extra : rebase_source : 863e9a46d04513ae8770da2dc9c406457fbda849
This commit is contained in:
Nikhil Marathe 2015-03-18 09:52:54 -07:00
Родитель dec7349527
Коммит 1084347046
3 изменённых файлов: 638 добавлений и 96 удалений

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

@ -502,7 +502,7 @@ GetRequiredScopeStringPrefix(const nsACString& aScriptSpec, nsACString& aPrefix)
} // anonymous namespace
class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
public nsIStreamLoaderObserver
public serviceWorkerScriptCache::CompareCallback
{
friend class ContinueInstallTask;
@ -587,60 +587,40 @@ public:
Update();
}
NS_IMETHOD
OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
nsresult aStatus, uint32_t aLen,
const uint8_t* aString) override
void
ComparisonResult(nsresult aStatus, bool aInCacheAndEqual) override
{
if (NS_WARN_IF(NS_FAILED(aStatus))) {
Fail(NS_ERROR_DOM_TYPE_ERR);
return aStatus;
return;
}
nsCOMPtr<nsIRequest> request;
nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
Fail(NS_ERROR_DOM_TYPE_ERR);
return rv;
if (aInCacheAndEqual) {
Succeed();
return;
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
if (!httpChannel) {
Fail(NS_ERROR_DOM_TYPE_ERR);
return NS_ERROR_FAILURE;
}
bool requestSucceeded;
rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_WARN_IF(NS_FAILED(rv) || !requestSucceeded)) {
Fail(NS_ERROR_DOM_TYPE_ERR);
return rv;
}
// FIXME(nsm): "Extract mime type..."
// FIXME(baku): The byte-by-byte check with the older script is performed by ScriptLoader.
NS_WARNING("Byte wise check is disabled, just using new one");
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
// FIXME: Bug 1130101 - Read max scope from Service-Worker-Allowed header.
nsAutoCString allowedPrefix;
rv = GetRequiredScopeStringPrefix(mRegistration->mScriptSpec, allowedPrefix);
nsresult rv = GetRequiredScopeStringPrefix(mRegistration->mScriptSpec, allowedPrefix);
if (NS_WARN_IF(NS_FAILED(rv))) {
Fail(NS_ERROR_DOM_SECURITY_ERR);
return rv;
return;
}
if (!StringBeginsWith(mRegistration->mScope, allowedPrefix)) {
NS_WARNING("By default a service worker's scope is restricted to at or below it's script's location.");
Fail(NS_ERROR_DOM_SECURITY_ERR);
return NS_ERROR_DOM_SECURITY_ERR;
return;
}
nsAutoString cacheName;
rv = serviceWorkerScriptCache::GenerateCacheName(cacheName);
if (NS_WARN_IF(NS_FAILED(rv))) {
Fail(NS_ERROR_DOM_TYPE_ERR);
return rv;
return;
}
// We have to create a ServiceWorker here simply to ensure there are no
@ -660,7 +640,7 @@ public:
if (NS_WARN_IF(NS_FAILED(rv))) {
swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
Fail(NS_ERROR_DOM_ABORT_ERR);
return rv;
return;
}
nsRefPtr<ServiceWorkerJob> upcasted = this;
@ -675,10 +655,8 @@ public:
if (NS_WARN_IF(!ok)) {
swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
Fail(NS_ERROR_DOM_ABORT_ERR);
return NS_ERROR_FAILURE;
return;
}
return NS_OK;
}
// Public so our error handling code can use it.
@ -774,42 +752,20 @@ private:
mRegistration->mInstallingWorker = nullptr;
}
// FIXME(nsm): Plug in FetchDriver when it is ready.
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), mRegistration->mScriptSpec, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return Fail(rv);
nsRefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest();
nsAutoString cacheName;
// 9.2.20 If newestWorker is not null, and newestWorker's script url is
// equal to registration's registering script url and response is a
// byte-for-byte match with the script resource of newestWorker...
if (workerInfo && workerInfo->ScriptSpec().Equals(mRegistration->mScriptSpec)) {
cacheName = workerInfo->CacheName();
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
mPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT); // FIXME(nsm): TYPE_SERVICEWORKER
if (NS_WARN_IF(NS_FAILED(rv))) {
return Fail(rv);
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
if (httpChannel) {
// Spec says no redirects allowed for SW scripts.
httpChannel->SetRedirectionLimit(0);
}
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
if (internalChannel) {
// Don't let serviceworker intercept.
internalChannel->ForceNoIntercept();
}
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return Fail(rv);
}
rv = channel->AsyncOpen(loader, nullptr);
nsresult rv =
serviceWorkerScriptCache::Compare(mRegistration->mPrincipal, cacheName,
NS_ConvertUTF8toUTF16(mRegistration->mScriptSpec),
this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return Fail(rv);
}

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

@ -3,17 +3,591 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ServiceWorkerScriptCache.h"
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/cache/Cache.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIPrincipal.h"
#include "Workers.h"
using mozilla::dom::cache::Cache;
using mozilla::dom::cache::CacheStorage;
BEGIN_WORKERS_NAMESPACE
namespace serviceWorkerScriptCache {
namespace {
already_AddRefed<CacheStorage>
CreateCacheStorage(nsIPrincipal* aPrincipal, ErrorResult& aRv)
{
AssertIsOnMainThread();
MOZ_ASSERT(aPrincipal);
nsIXPConnect* xpc = nsContentUtils::XPConnect();
MOZ_ASSERT(xpc, "This should never be null!");
AutoJSAPI jsapi;
jsapi.Init();
nsCOMPtr<nsIXPConnectJSObjectHolder> sandbox;
aRv = xpc->CreateSandbox(jsapi.cx(), aPrincipal, getter_AddRefs(sandbox));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nsCOMPtr<nsIGlobalObject> sandboxGlobalObject =
xpc::NativeGlobal(sandbox->GetJSObject());
if (!sandboxGlobalObject) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return CacheStorage::CreateOnMainThread(cache::CHROME_ONLY_NAMESPACE,
sandboxGlobalObject,
aPrincipal, aRv);
}
class CompareManager;
// This class downloads a URL from the network and then it calls
// NetworkFinished() in the CompareManager.
class CompareNetwork final : public nsIStreamLoaderObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLOADEROBSERVER
explicit CompareNetwork(CompareManager* aManager)
: mManager(aManager)
{
MOZ_ASSERT(aManager);
AssertIsOnMainThread();
}
nsresult
Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL)
{
MOZ_ASSERT(aPrincipal);
AssertIsOnMainThread();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = NS_NewChannel(getter_AddRefs(mChannel),
uri, aPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_SCRIPT); // FIXME(nsm): TYPE_SERVICEWORKER
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// FIXME(nsm): Set redirect limit.
// Don't let serviceworker intercept.
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(mChannel);
if (internalChannel) {
internalChannel->ForceNoIntercept();
}
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mChannel->AsyncOpen(loader, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
void
Abort()
{
AssertIsOnMainThread();
MOZ_ASSERT(mChannel);
mChannel->Cancel(NS_BINDING_ABORTED);
mChannel = nullptr;
}
const nsString& Buffer() const
{
AssertIsOnMainThread();
return mBuffer;
}
private:
~CompareNetwork()
{
AssertIsOnMainThread();
}
nsRefPtr<CompareManager> mManager;
nsCOMPtr<nsIChannel> mChannel;
nsString mBuffer;
};
NS_IMPL_ISUPPORTS(CompareNetwork, nsIStreamLoaderObserver)
// This class gets a cached Response from the CacheStorage and then it calls
// CacheFinished() in the CompareManager.
class CompareCache final : public PromiseNativeHandler
, public nsIStreamLoaderObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLOADEROBSERVER
explicit CompareCache(CompareManager* aManager)
: mManager(aManager)
, mState(WaitingForCache)
, mAborted(false)
{
MOZ_ASSERT(aManager);
AssertIsOnMainThread();
}
nsresult
Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
const nsAString& aCacheName)
{
MOZ_ASSERT(aPrincipal);
AssertIsOnMainThread();
mURL = aURL;
ErrorResult rv;
nsRefPtr<CacheStorage> cacheStorage = CreateCacheStorage(aPrincipal, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.ErrorCode();
}
nsRefPtr<Promise> promise = cacheStorage->Open(aCacheName, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.ErrorCode();
}
promise->AppendNativeHandler(this);
return NS_OK;
}
void
Abort()
{
AssertIsOnMainThread();
MOZ_ASSERT(!mAborted);
mAborted = true;
if (mPump) {
mPump->Cancel(NS_BINDING_ABORTED);
mPump = nullptr;
}
}
// This class manages 2 promises: 1 is to retrieve cache object, and 2 is for
// the value from the cache. For this reason we have mState to know what
// reject/resolve callback we are handling.
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
{
AssertIsOnMainThread();
if (mAborted) {
return;
}
if (mState == WaitingForCache) {
ManageCacheResult(aCx, aValue);
return;
}
MOZ_ASSERT(mState == WaitingForValue);
ManageValueResult(aCx, aValue);
}
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
const nsString& Buffer() const
{
AssertIsOnMainThread();
return mBuffer;
}
private:
~CompareCache()
{
AssertIsOnMainThread();
}
void
ManageCacheResult(JSContext* aCx, JS::Handle<JS::Value> aValue);
void
ManageValueResult(JSContext* aCx, JS::Handle<JS::Value> aValue);
nsRefPtr<CompareManager> mManager;
nsCOMPtr<nsIInputStreamPump> mPump;
nsString mURL;
nsString mBuffer;
enum {
WaitingForCache,
WaitingForValue
} mState;
bool mAborted;
};
NS_IMPL_ISUPPORTS(CompareCache, nsIStreamLoaderObserver)
class CompareManager final
{
public:
NS_INLINE_DECL_REFCOUNTING(CompareManager)
explicit CompareManager(CompareCallback* aCallback)
: mCallback(aCallback)
, mNetworkFinished(false)
, mCacheFinished(false)
, mInCache(false)
{
AssertIsOnMainThread();
}
nsresult
Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
const nsAString& aCacheName)
{
AssertIsOnMainThread();
MOZ_ASSERT(aPrincipal);
mCN = new CompareNetwork(this);
nsresult rv = mCN->Initialize(aPrincipal, aURL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!aCacheName.IsEmpty()) {
mCC = new CompareCache(this);
mCC->Initialize(aPrincipal, aURL, aCacheName);
if (NS_WARN_IF(NS_FAILED(rv))) {
mCN->Abort();
return rv;
}
}
return NS_OK;
}
void
NetworkFinished(nsresult aStatus)
{
AssertIsOnMainThread();
mNetworkFinished = true;
if (NS_FAILED(aStatus)) {
if (mCC) {
mCC->Abort();
}
ComparisonFinished(aStatus, false);
return;
}
MaybeCompare();
}
void
CacheFinished(nsresult aStatus, bool aInCache)
{
AssertIsOnMainThread();
mCacheFinished = true;
mInCache = aInCache;
if (NS_FAILED(aStatus)) {
if (mCN) {
mCN->Abort();
}
ComparisonFinished(aStatus, false);
return;
}
MaybeCompare();
}
void
MaybeCompare()
{
AssertIsOnMainThread();
if (!mNetworkFinished || (mCC && !mCacheFinished)) {
return;
}
if (!mCC || !mInCache) {
ComparisonFinished(NS_OK, false);
return;
}
ComparisonFinished(NS_OK, mCC->Buffer().Equals(mCN->Buffer()));
}
private:
~CompareManager()
{
AssertIsOnMainThread();
MOZ_ASSERT(!mCC);
MOZ_ASSERT(!mCN);
}
void
ComparisonFinished(nsresult aStatus, bool aIsEqual)
{
AssertIsOnMainThread();
MOZ_ASSERT(mCallback);
mCallback->ComparisonResult(aStatus, aIsEqual);
mCallback = nullptr;
mCN = nullptr;
mCC = nullptr;
}
nsRefPtr<CompareCallback> mCallback;
nsRefPtr<CompareNetwork> mCN;
nsRefPtr<CompareCache> mCC;
bool mNetworkFinished;
bool mCacheFinished;
bool mInCache;
};
NS_IMETHODIMP
CompareNetwork::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
nsresult aStatus, uint32_t aLen,
const uint8_t* aString)
{
AssertIsOnMainThread();
// If no channel, Abort() has been called.
if (!mChannel) {
return NS_OK;
}
if (NS_WARN_IF(NS_FAILED(aStatus))) {
mManager->NetworkFinished(aStatus);
return NS_OK;
}
nsCOMPtr<nsIRequest> request;
nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(rv);
return NS_OK;
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
if (!httpChannel) {
mManager->NetworkFinished(NS_ERROR_FAILURE);
return NS_OK;
}
bool requestSucceeded;
rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(rv);
return NS_OK;
}
if (!requestSucceeded) {
mManager->NetworkFinished(NS_ERROR_FAILURE);
return NS_OK;
}
// FIXME(nsm): "Extract mime type..."
char16_t* buffer = nullptr;
size_t len = 0;
rv = nsScriptLoader::ConvertToUTF16(httpChannel, aString, aLen,
NS_LITERAL_STRING("UTF-8"), nullptr,
buffer, len);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(rv);
return rv;
}
mBuffer.Adopt(buffer, len);
mManager->NetworkFinished(NS_OK);
return NS_OK;
}
NS_IMETHODIMP
CompareCache::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
nsresult aStatus, uint32_t aLen,
const uint8_t* aString)
{
AssertIsOnMainThread();
if (!mAborted) {
return aStatus;
}
if (NS_WARN_IF(NS_FAILED(aStatus))) {
mManager->CacheFinished(aStatus, false);
return aStatus;
}
char16_t* buffer = nullptr;
size_t len = 0;
nsresult rv = nsScriptLoader::ConvertToUTF16(nullptr, aString, aLen,
NS_LITERAL_STRING("UTF-8"),
nullptr, buffer, len);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->CacheFinished(rv, false);
return rv;
}
mBuffer.Adopt(buffer, len);
mManager->CacheFinished(NS_OK, true);
return NS_OK;
}
void
CompareCache::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
AssertIsOnMainThread();
if (mAborted) {
return;
}
mManager->CacheFinished(NS_ERROR_FAILURE, false);
}
void
CompareCache::ManageCacheResult(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
AssertIsOnMainThread();
if (NS_WARN_IF(!aValue.isObject())) {
mManager->CacheFinished(NS_ERROR_FAILURE, false);
return;
}
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
if (NS_WARN_IF(!obj)) {
mManager->CacheFinished(NS_ERROR_FAILURE, false);
return;
}
Cache* cache = nullptr;
nsresult rv = UNWRAP_OBJECT(Cache, obj, cache);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->CacheFinished(rv, false);
return;
}
RequestOrUSVString request;
request.SetAsUSVString().Rebind(mURL.Data(), mURL.Length());
ErrorResult error;
CacheQueryOptions params;
nsRefPtr<Promise> promise = cache->Match(request, params, error);
if (NS_WARN_IF(error.Failed())) {
mManager->CacheFinished(error.ErrorCode(), false);
return;
}
promise->AppendNativeHandler(this);
mState = WaitingForValue;
}
void
CompareCache::ManageValueResult(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
AssertIsOnMainThread();
// The cache returns undefined if the object is not stored.
if (aValue.isUndefined()) {
mManager->CacheFinished(NS_OK, false);
return;
}
MOZ_ASSERT(aValue.isObject());
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
if (NS_WARN_IF(!obj)) {
mManager->CacheFinished(NS_ERROR_FAILURE, false);
return;
}
Response* response = nullptr;
nsresult rv = UNWRAP_OBJECT(Response, obj, response);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->CacheFinished(rv, false);
return;
}
MOZ_ASSERT(response->Ok());
nsCOMPtr<nsIInputStream> inputStream;
response->GetBody(getter_AddRefs(inputStream));
MOZ_ASSERT(inputStream);
MOZ_ASSERT(!mPump);
rv = NS_NewInputStreamPump(getter_AddRefs(mPump), inputStream);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->CacheFinished(rv, false);
return;
}
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->CacheFinished(rv, false);
return;
}
rv = mPump->AsyncRead(loader, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPump = nullptr;
mManager->CacheFinished(rv, false);
return;
}
nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(mPump);
if (rr) {
nsCOMPtr<nsIEventTarget> sts =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
rv = rr->RetargetDeliveryTo(sts);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPump = nullptr;
mManager->CacheFinished(rv, false);
return;
}
}
}
} // anonymous namespace
nsresult
PurgeCache(nsIPrincipal* aPrincipal, const nsAString& aCacheName)
{
@ -24,37 +598,17 @@ PurgeCache(nsIPrincipal* aPrincipal, const nsAString& aCacheName)
return NS_OK;
}
nsIXPConnect* xpc = nsContentUtils::XPConnect();
MOZ_ASSERT(xpc, "This should never be null!");
AutoJSAPI jsapi;
jsapi.Init();
nsCOMPtr<nsIXPConnectJSObjectHolder> sandbox;
nsresult rv = xpc->CreateSandbox(jsapi.cx(), aPrincipal, getter_AddRefs(sandbox));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIGlobalObject> sandboxGlobalObject =
xpc::NativeGlobal(sandbox->GetJSObject());
if (!sandboxGlobalObject) {
return NS_ERROR_FAILURE;
}
ErrorResult error;
nsRefPtr<CacheStorage> cacheStorage =
CacheStorage::CreateOnMainThread(cache::CHROME_ONLY_NAMESPACE,
sandboxGlobalObject,
aPrincipal, error);
if (NS_WARN_IF(error.Failed())) {
return error.ErrorCode();
ErrorResult rv;
nsRefPtr<CacheStorage> cacheStorage = CreateCacheStorage(aPrincipal, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.ErrorCode();
}
// We use the ServiceWorker scope as key for the cacheStorage.
nsRefPtr<Promise> promise =
cacheStorage->Delete(aCacheName, error);
if (NS_WARN_IF(error.Failed())) {
return error.ErrorCode();
cacheStorage->Delete(aCacheName, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.ErrorCode();
}
// We don't actually care about the result of the delete operation.
@ -84,6 +638,25 @@ GenerateCacheName(nsAString& aName)
return NS_OK;
}
nsresult
Compare(nsIPrincipal* aPrincipal, const nsAString& aCacheName,
const nsAString& aURL, CompareCallback* aCallback)
{
AssertIsOnMainThread();
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(!aURL.IsEmpty());
MOZ_ASSERT(aCallback);
nsRefPtr<CompareManager> cm = new CompareManager(aCallback);
nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
} // serviceWorkerScriptCache namespace
END_WORKERS_NAMESPACE

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

@ -21,6 +21,19 @@ PurgeCache(nsIPrincipal* aPrincipal, const nsAString& aCacheName);
nsresult
GenerateCacheName(nsAString& aName);
class CompareCallback
{
public:
virtual void ComparisonResult(nsresult aStatus, bool aInCacheAndEqual) = 0;
virtual MozExternalRefCountType AddRef() = 0;
virtual MozExternalRefCountType Release() = 0;
};
nsresult
Compare(nsIPrincipal* aPrincipal, const nsAString& aCacheName,
const nsAString& aURL, CompareCallback* aCallback);
} // serviceWorkerScriptCache namespace
} // workers namespace