221 lines
7.7 KiB
C++
221 lines
7.7 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef mozilla_dom_workers_CacheLoadHandler_h__
|
|
#define mozilla_dom_workers_CacheLoadHandler_h__
|
|
|
|
#include "nsIContentPolicy.h"
|
|
#include "nsIInputStreamPump.h"
|
|
#include "nsIStreamLoader.h"
|
|
#include "nsStringFwd.h"
|
|
#include "nsStreamUtils.h"
|
|
|
|
#include "mozilla/StaticPrefs_browser.h"
|
|
#include "mozilla/dom/CacheBinding.h"
|
|
#include "mozilla/dom/ChannelInfo.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/PromiseNativeHandler.h"
|
|
#include "mozilla/dom/ScriptLoadHandler.h"
|
|
#include "mozilla/dom/cache/Cache.h"
|
|
#include "mozilla/dom/cache/CacheStorage.h"
|
|
#include "mozilla/dom/WorkerCommon.h"
|
|
#include "mozilla/dom/WorkerRef.h"
|
|
|
|
#include "mozilla/dom/workerinternals/ScriptLoader.h"
|
|
|
|
using mozilla::dom::cache::Cache;
|
|
using mozilla::dom::cache::CacheStorage;
|
|
using mozilla::ipc::PrincipalInfo;
|
|
|
|
namespace mozilla::dom {
|
|
|
|
class WorkerLoadContext;
|
|
|
|
namespace workerinternals::loader {
|
|
|
|
/*
|
|
* [DOMDOC] CacheLoadHandler for Workers
|
|
*
|
|
* A LoadHandler is a ScriptLoader helper class that reacts to an
|
|
* nsIStreamLoader's events for loading JS scripts. It is primarily responsible
|
|
* for decoding the stream into UTF8 or UTF16. Additionally, it takes care of
|
|
* any work that needs to follow the completion of a stream. Every LoadHandler
|
|
* also manages additional tasks for the type of load that it is doing.
|
|
*
|
|
* CacheLoadHandler is a specialized LoadHandler used by ServiceWorkers to
|
|
* implement the installation model used by ServiceWorkers to support running
|
|
* offline. When a ServiceWorker is installed, its main script is evaluated and
|
|
* all script resources that are loaded are saved. The spec does not specify the
|
|
* storage mechanism for this, but we chose to reuse the Cache API[1] mechanism
|
|
* that we expose to content to also store the script and its dependencies. We
|
|
* store the script resources in a special chrome namespace CacheStorage that is
|
|
* not visible to content. Each distinct ServiceWorker installation gets its own
|
|
* Cache keyed by a randomly-generated UUID.
|
|
*
|
|
* In terms of specification, this class implements step 4 of
|
|
* https://w3c.github.io/ServiceWorker/#importscripts
|
|
*
|
|
* Relationship to NetworkLoadHandler
|
|
*
|
|
* During ServiceWorker installation, the CacheLoadHandler falls back on the
|
|
* NetworkLoadHandler by calling `mLoader->LoadScript(...)`. If a script has not
|
|
* been seen before, then we will fall back on loading from the network.
|
|
* However, if the ServiceWorker is already installed, an error will be
|
|
* generated and the ServiceWorker will fail to load, per spec.
|
|
*
|
|
* CacheLoadHandler does not persist some pieces of information, such as the
|
|
* sourceMapUrl. Also, the DOM Cache API storage does not yet support alternate
|
|
* data streams for JS Bytecode or WASM caching; this is tracked by Bug 1336199.
|
|
*
|
|
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/caches
|
|
*
|
|
*/
|
|
|
|
class CacheLoadHandler final : public PromiseNativeHandler,
|
|
public nsIStreamLoaderObserver {
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSISTREAMLOADEROBSERVER
|
|
|
|
CacheLoadHandler(ThreadSafeWorkerRef* aWorkerRef,
|
|
ThreadSafeRequestHandle* aRequestHandle,
|
|
bool aIsWorkerScript,
|
|
bool aOnlyExistingCachedResourcesAllowed,
|
|
WorkerScriptLoader* aLoader);
|
|
|
|
void Fail(nsresult aRv);
|
|
|
|
void Load(Cache* aCache);
|
|
|
|
virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|
ErrorResult& aRv) override;
|
|
|
|
virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|
ErrorResult& aRv) override;
|
|
|
|
private:
|
|
~CacheLoadHandler() { AssertIsOnMainThread(); }
|
|
|
|
nsresult DataReceivedFromCache(const uint8_t* aString, uint32_t aStringLen,
|
|
const mozilla::dom::ChannelInfo& aChannelInfo,
|
|
UniquePtr<PrincipalInfo> aPrincipalInfo,
|
|
const nsACString& aCSPHeaderValue,
|
|
const nsACString& aCSPReportOnlyHeaderValue,
|
|
const nsACString& aReferrerPolicyHeaderValue);
|
|
nsresult DataReceived();
|
|
|
|
RefPtr<ThreadSafeRequestHandle> mRequestHandle;
|
|
const RefPtr<WorkerScriptLoader> mLoader;
|
|
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
|
|
const bool mIsWorkerScript;
|
|
bool mFailed;
|
|
bool mOnlyExistingCachedResourcesAllowed;
|
|
nsCOMPtr<nsIInputStreamPump> mPump;
|
|
nsCOMPtr<nsIURI> mBaseURI;
|
|
mozilla::dom::ChannelInfo mChannelInfo;
|
|
UniquePtr<PrincipalInfo> mPrincipalInfo;
|
|
UniquePtr<ScriptDecoder> mDecoder;
|
|
nsCString mCSPHeaderValue;
|
|
nsCString mCSPReportOnlyHeaderValue;
|
|
nsCString mReferrerPolicyHeaderValue;
|
|
nsCOMPtr<nsISerialEventTarget> mMainThreadEventTarget;
|
|
};
|
|
|
|
/*
|
|
* CacheCreator
|
|
*
|
|
* The CacheCreator is responsible for maintaining a CacheStorage for the
|
|
* purposes of caching ServiceWorkers (see comment on CacheLoadHandler). In
|
|
* addition, it tracks all CacheLoadHandlers and is used for cleanup once
|
|
* loading has finished.
|
|
*
|
|
*/
|
|
|
|
class CacheCreator final : public PromiseNativeHandler {
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
explicit CacheCreator(WorkerPrivate* aWorkerPrivate);
|
|
|
|
void AddLoader(MovingNotNull<RefPtr<CacheLoadHandler>> aLoader) {
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(!mCacheStorage);
|
|
mLoaders.AppendElement(std::move(aLoader));
|
|
}
|
|
|
|
virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|
ErrorResult& aRv) override;
|
|
|
|
virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|
ErrorResult& aRv) override;
|
|
|
|
// Try to load from cache with aPrincipal used for cache access.
|
|
nsresult Load(nsIPrincipal* aPrincipal);
|
|
|
|
Cache* Cache_() const {
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(mCache);
|
|
return mCache;
|
|
}
|
|
|
|
nsIGlobalObject* Global() const {
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(mSandboxGlobalObject);
|
|
return mSandboxGlobalObject;
|
|
}
|
|
|
|
void DeleteCache(nsresult aReason);
|
|
|
|
private:
|
|
~CacheCreator() = default;
|
|
|
|
nsresult CreateCacheStorage(nsIPrincipal* aPrincipal);
|
|
|
|
void FailLoaders(nsresult aRv);
|
|
|
|
RefPtr<Cache> mCache;
|
|
RefPtr<CacheStorage> mCacheStorage;
|
|
nsCOMPtr<nsIGlobalObject> mSandboxGlobalObject;
|
|
nsTArray<NotNull<RefPtr<CacheLoadHandler>>> mLoaders;
|
|
|
|
nsString mCacheName;
|
|
OriginAttributes mOriginAttributes;
|
|
};
|
|
|
|
/*
|
|
* CachePromiseHandler
|
|
*
|
|
* This promise handler is used to track if a ServiceWorker has been written to
|
|
* Cache. It is responsible for tracking the state of the ServiceWorker being
|
|
* cached. It also handles cancelling caching of a ServiceWorker if loading is
|
|
* interrupted. It is initialized by the NetworkLoadHandler as part of the first
|
|
* load of a ServiceWorker.
|
|
*
|
|
*/
|
|
class CachePromiseHandler final : public PromiseNativeHandler {
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
CachePromiseHandler(WorkerScriptLoader* aLoader,
|
|
ThreadSafeRequestHandle* aRequestHandle);
|
|
|
|
virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|
ErrorResult& aRv) override;
|
|
|
|
virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|
ErrorResult& aRv) override;
|
|
|
|
private:
|
|
~CachePromiseHandler() { AssertIsOnMainThread(); }
|
|
|
|
RefPtr<WorkerScriptLoader> mLoader;
|
|
RefPtr<ThreadSafeRequestHandle> mRequestHandle;
|
|
};
|
|
|
|
} // namespace workerinternals::loader
|
|
} // namespace mozilla::dom
|
|
|
|
#endif /* mozilla_dom_workers_CacheLoadHandler_h__ */
|