This reverts commit1abc6298e1. Revert "Bug 2056134 - Part 5: Add tests. r=bthrall" This reverts commit7bb9240798. Revert "Bug 2056134 - Part 4: Use pre-calculated fallback encoding for the classic script disk cache MIME-Type. r=bthrall" This reverts commit07b098aa0d. Revert "Bug 2056134 - Part 3: Use pre-calculated fallback encoding for the in-memory cache key. r=bthrall" This reverts commit3173e731bf. Revert "Bug 2056134 - Part 2: Pre-calculate the fallback encoding for the classic script source. r=bthrall,dom-worker-reviewers,edenchuang" This reverts commitfd0eb69272. Revert "Bug 2056134 - Part 1: Include the hint charset into the cache key string for the ChromeUtils.getCachedJavaScriptSource. r=bthrall,devtools-reviewers,jdescottes" This reverts commitc0bc4e330d.
362 lines
13 KiB
C++
362 lines
13 KiB
C++
/* 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_SharedScriptCache_h
|
|
#define mozilla_dom_SharedScriptCache_h
|
|
|
|
#include "PLDHashTable.h" // PLDHashEntryHdr
|
|
#include "js/TypeDecls.h" // JSContext, JS::MutableHandle, JS::Value
|
|
#include "js/loader/LoadedScript.h" // JS::loader::LoadedScript
|
|
#include "js/loader/ScriptFetchOptions.h" // JS::loader::ScriptFetchOptions
|
|
#include "js/loader/ScriptKind.h" // JS::loader::ScriptKind
|
|
#include "js/loader/ScriptLoadRequest.h" // JS::loader::ScriptLoadRequest
|
|
#include "mozilla/CORSMode.h" // mozilla::CORSMode
|
|
#include "mozilla/HashTable.h" // mozilla::HashMap
|
|
#include "mozilla/MemoryReporting.h" // MallocSizeOf
|
|
#include "mozilla/Mutex.h" // Mutex, GUARDED_BY, MutexAutoLock
|
|
#include "mozilla/RefPtr.h" // RefPtr
|
|
#include "mozilla/SharedSubResourceCache.h" // SharedSubResourceCache, SharedSubResourceCacheLoadingValueBase, SubResourceNetworkMetadataHolder
|
|
#include "mozilla/ThreadSafety.h" // MOZ_GUARDED_BY
|
|
#include "mozilla/UniquePtr.h" // mozilla::UniquePtr
|
|
#include "mozilla/WeakPtr.h" // SupportsWeakPtr
|
|
#include "mozilla/dom/CacheExpirationTime.h" // CacheExpirationTime
|
|
#include "nsIMemoryReporter.h" // nsIMemoryReporter, NS_DECL_NSIMEMORYREPORTER
|
|
#include "nsIObserver.h" // nsIObserver
|
|
#include "nsIPrincipal.h" // nsIPrincipal
|
|
#include "nsISupports.h" // nsISupports, NS_DECL_ISUPPORTS
|
|
#include "nsStringFwd.h" // nsACString
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
class ScriptLoader;
|
|
class ScriptLoadData;
|
|
|
|
class ScriptHashKey : public PLDHashEntryHdr {
|
|
public:
|
|
using KeyType = const ScriptHashKey&;
|
|
using KeyTypePointer = const ScriptHashKey*;
|
|
|
|
explicit ScriptHashKey(const ScriptHashKey& aKey)
|
|
: PLDHashEntryHdr(),
|
|
mURI(aKey.mURI),
|
|
mPartitionPrincipal(aKey.mPartitionPrincipal),
|
|
mLoaderPrincipal(aKey.mLoaderPrincipal),
|
|
mKind(aKey.mKind),
|
|
mCORSMode(aKey.mCORSMode),
|
|
mReferrerPolicy(aKey.mReferrerPolicy),
|
|
mHintCharset(aKey.mHintCharset) {
|
|
MOZ_COUNT_CTOR(ScriptHashKey);
|
|
}
|
|
|
|
explicit ScriptHashKey(const ScriptHashKey* aKey) : ScriptHashKey(*aKey) {}
|
|
|
|
ScriptHashKey(ScriptHashKey&& aKey)
|
|
: PLDHashEntryHdr(),
|
|
mURI(std::move(aKey.mURI)),
|
|
mPartitionPrincipal(std::move(aKey.mPartitionPrincipal)),
|
|
mLoaderPrincipal(std::move(aKey.mLoaderPrincipal)),
|
|
mKind(std::move(aKey.mKind)),
|
|
mCORSMode(std::move(aKey.mCORSMode)),
|
|
mReferrerPolicy(std::move(aKey.mReferrerPolicy)),
|
|
mHintCharset(std::move(aKey.mHintCharset)) {
|
|
MOZ_COUNT_CTOR(ScriptHashKey);
|
|
}
|
|
|
|
ScriptHashKey(ScriptLoader* aLoader,
|
|
const JS::loader::ScriptLoadRequest* aRequest,
|
|
mozilla::dom::ReferrerPolicy aReferrerPolicy,
|
|
const JS::loader::ScriptFetchOptions* aFetchOptions,
|
|
const nsCOMPtr<nsIURI> aURI);
|
|
explicit ScriptHashKey(const ScriptLoadData& aLoadData);
|
|
|
|
// Create a key which can be used only for lookup.
|
|
// aKey is the result of ToStringForLookup.
|
|
static Maybe<ScriptHashKey> FromStringsForLookup(
|
|
const nsACString& aKey, const nsACString& aURI,
|
|
const nsACString& aHintCharset);
|
|
|
|
private:
|
|
ScriptHashKey(nsIURI* aURI, nsIPrincipal* aPartitionPrincipal,
|
|
JS::loader::ScriptKind aKind, CORSMode aCORSMode,
|
|
mozilla::dom::ReferrerPolicy aReferrerPolicy,
|
|
const nsString& aHintCharset)
|
|
: PLDHashEntryHdr(),
|
|
mURI(aURI),
|
|
mPartitionPrincipal(aPartitionPrincipal),
|
|
mLoaderPrincipal(nullptr),
|
|
mKind(aKind),
|
|
mCORSMode(aCORSMode),
|
|
mReferrerPolicy(aReferrerPolicy),
|
|
mHintCharset(aHintCharset) {
|
|
MOZ_COUNT_CTOR(ScriptHashKey);
|
|
}
|
|
|
|
public:
|
|
MOZ_COUNTED_DTOR(ScriptHashKey)
|
|
|
|
const ScriptHashKey& GetKey() const { return *this; }
|
|
const ScriptHashKey* GetKeyPointer() const { return this; }
|
|
|
|
bool KeyEquals(const ScriptHashKey* aKey) const { return KeyEquals(*aKey); }
|
|
|
|
bool KeyEquals(const ScriptHashKey&) const;
|
|
|
|
static const ScriptHashKey* KeyToPointer(const ScriptHashKey& aKey) {
|
|
return &aKey;
|
|
}
|
|
static PLDHashNumber HashKey(const ScriptHashKey* aKey) {
|
|
return nsURIHashKey::HashKey(aKey->mURI);
|
|
}
|
|
|
|
nsIPrincipal* LoaderPrincipal() const { return mLoaderPrincipal; }
|
|
nsIPrincipal* PartitionPrincipal() const { return mPartitionPrincipal; }
|
|
|
|
nsIURI* URI() const { return mURI; }
|
|
|
|
enum { ALLOW_MEMMOVE = true };
|
|
|
|
// Stringifies this key's information for the aKey parameter for the
|
|
// FromStringsForLookup.
|
|
// This stringifies a subset of the fields, which cannot be directly
|
|
// extracted from the channel.
|
|
void ToStringForLookup(nsACString& aResult);
|
|
|
|
protected:
|
|
// Order the fields from the most important one as much as possible, while
|
|
// packing them, in order to use the same order between the definition and
|
|
// the KeyEquals implementation.
|
|
|
|
// The script's URI. This should distinguish the cache entry in most case.
|
|
const nsCOMPtr<nsIURI> mURI;
|
|
|
|
// If single content process has multiple principals, mPartitionPrincipal
|
|
// should distinguish them.
|
|
const nsCOMPtr<nsIPrincipal> mPartitionPrincipal;
|
|
|
|
// NOTE: mLoaderPrincipal is only for SharedSubResourceCache logic,
|
|
// and not part of KeyEquals.
|
|
const nsCOMPtr<nsIPrincipal> mLoaderPrincipal;
|
|
|
|
// Other fields should be unique per each script in general.
|
|
const JS::loader::ScriptKind mKind;
|
|
const CORSMode mCORSMode;
|
|
const mozilla::dom::ReferrerPolicy mReferrerPolicy;
|
|
|
|
// charset attribute for classic script.
|
|
// module always use UTF-8.
|
|
nsString mHintCharset;
|
|
};
|
|
|
|
class ScriptLoadData final
|
|
: public SupportsWeakPtr,
|
|
public nsISupports,
|
|
public SharedSubResourceCacheLoadingValueBase<ScriptLoadData> {
|
|
protected:
|
|
~ScriptLoadData() = default;
|
|
|
|
public:
|
|
ScriptLoadData(ScriptLoader* aLoader, JS::loader::ScriptLoadRequest* aRequest,
|
|
CacheExpirationTime aExpirationTime,
|
|
JS::loader::LoadedScript* aLoadedScript);
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// Only completed loads are used for the cache.
|
|
bool IsLoading() const override { return false; }
|
|
bool IsCancelled() const override { return false; }
|
|
bool IsSyncLoad() const override { return true; }
|
|
|
|
SubResourceNetworkMetadataHolder* GetNetworkMetadata() const override {
|
|
return mNetworkMetadata.get();
|
|
}
|
|
|
|
void StartLoading() override {}
|
|
void SetLoadCompleted() override {}
|
|
void OnCoalescedTo(const ScriptLoadData& aExistingLoad) override {}
|
|
void Cancel() override {}
|
|
|
|
void DidCancelLoad() {}
|
|
|
|
bool ShouldDefer() const { return false; }
|
|
|
|
JS::loader::LoadedScript* ValueForCache() const {
|
|
return mLoadedScript.get();
|
|
}
|
|
|
|
const CacheExpirationTime& ExpirationTime() const { return mExpirationTime; }
|
|
|
|
ScriptLoader& Loader() { return *mLoader; }
|
|
|
|
const ScriptHashKey& CacheKey() const { return mKey; }
|
|
|
|
private:
|
|
CacheExpirationTime mExpirationTime = CacheExpirationTime::Never();
|
|
ScriptLoader* mLoader;
|
|
ScriptHashKey mKey;
|
|
RefPtr<JS::loader::LoadedScript> mLoadedScript;
|
|
RefPtr<SubResourceNetworkMetadataHolder> mNetworkMetadata;
|
|
};
|
|
|
|
struct SharedScriptCacheTraits {
|
|
using Loader = ScriptLoader;
|
|
using Key = ScriptHashKey;
|
|
using Value = JS::loader::LoadedScript;
|
|
using LoadingValue = ScriptLoadData;
|
|
|
|
static ScriptHashKey KeyFromLoadingValue(const LoadingValue& aValue) {
|
|
return ScriptHashKey(aValue);
|
|
}
|
|
};
|
|
|
|
class SharedScriptCache final
|
|
: public SharedSubResourceCache<SharedScriptCacheTraits, SharedScriptCache>,
|
|
public nsIMemoryReporter,
|
|
public nsIObserver {
|
|
public:
|
|
using Base =
|
|
SharedSubResourceCache<SharedScriptCacheTraits, SharedScriptCache>;
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIMEMORYREPORTER
|
|
|
|
SharedScriptCache();
|
|
void Init();
|
|
|
|
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* aData) override {
|
|
if (strcmp(aTopic, "ipc:content-shutdown") == 0) {
|
|
OnContentShutdown(aSubject);
|
|
return NS_OK;
|
|
}
|
|
if (strcmp(aTopic, "profile-before-change") == 0) {
|
|
OnProfileBeforeChange();
|
|
return NS_OK;
|
|
}
|
|
return Base::DoObserve(aSubject, aTopic, aData);
|
|
}
|
|
|
|
bool MaybeScheduleUpdateDiskCache();
|
|
void UpdateDiskCache();
|
|
|
|
void EncodeAndCompress();
|
|
void SaveToDiskCache();
|
|
|
|
void InvalidateInProcess();
|
|
|
|
void OnEntryInserted();
|
|
void OnEntryEverHit();
|
|
|
|
void UpdateEverHitTelemetry();
|
|
static void RecvUpdateEverHitTelemetry(const uint64_t& aChildId,
|
|
const uint32_t& aRate);
|
|
|
|
// This has to be static because it's also called for loaders that don't have
|
|
// a sheet cache (loaders that are not owned by a document).
|
|
static void LoadCompleted(SharedScriptCache*, ScriptLoadData&);
|
|
using Base::LoadCompleted;
|
|
static void Clear(const Maybe<bool>& aChrome = Nothing(),
|
|
const Maybe<nsCOMPtr<nsIPrincipal>>& aPrincipal = Nothing(),
|
|
const Maybe<nsCString>& aSchemelessSite = Nothing(),
|
|
const Maybe<OriginAttributesPattern>& aPattern = Nothing(),
|
|
const Maybe<nsCString>& aURL = Nothing());
|
|
|
|
static void Invalidate();
|
|
|
|
static bool GetCachedScriptSource(JSContext* aCx, const nsACString& aKey,
|
|
const nsACString& aURI,
|
|
const nsACString& aHintCharset,
|
|
JS::MutableHandle<JS::Value> aRetval);
|
|
|
|
static void PrepareForLastCC();
|
|
|
|
protected:
|
|
~SharedScriptCache();
|
|
|
|
bool ShouldIgnoreMemoryPressure() override;
|
|
|
|
void ClearInProcessForMemoryPressure() override;
|
|
|
|
private:
|
|
bool EnsureEverHitMap();
|
|
void OnContentShutdown(nsISupports* aSubject);
|
|
void OnProfileBeforeChange();
|
|
void AccumulateEverHitTelemetry(uint32_t aRate);
|
|
|
|
void SetDiskCacheTimer();
|
|
void ClearDiskCacheTimer();
|
|
void OnDiskCacheTimer();
|
|
|
|
class EncodeItem {
|
|
public:
|
|
EncodeItem(JS::Stencil* aStencil, JS::TranscodeBuffer&& aSRI,
|
|
JS::loader::LoadedScript* aLoadedScript)
|
|
: mStencil(aStencil),
|
|
mSRI(std::move(aSRI)),
|
|
mLoadedScript(aLoadedScript) {}
|
|
|
|
// These fields can be touched from multiple threads.
|
|
RefPtr<JS::Stencil> mStencil;
|
|
JS::TranscodeBuffer mSRI;
|
|
Vector<uint8_t> mCompressed;
|
|
|
|
// This can be dereferenced only from the main thread.
|
|
// Reading the pointer itself is allowed also off main thread.
|
|
RefPtr<JS::loader::LoadedScript> mLoadedScript;
|
|
};
|
|
|
|
// Set to true if the telemetry data is sent from the content process and
|
|
// the preparation for the telemetry accumulation/submission is done.
|
|
// This is set to true even if the preparation fails (mEverHitMap == nullptr),
|
|
// to avoid retrying the preparation again and again.
|
|
//
|
|
// This field is used only on the parent process.
|
|
bool mPreparedEverHitMap = false;
|
|
|
|
// True if the disk cache timer should be rescheduled.
|
|
// This is set to true if another activity happens during the timer
|
|
// is set.
|
|
bool mRetryDiskCacheTimer = false;
|
|
|
|
// The initial value for mLastEverHitRatio, which is outside of the
|
|
// valid range.
|
|
static constexpr uint32_t NOT_YET_REPORTED = 101;
|
|
|
|
// The cache-hit ratio value that's sent to the parent process.
|
|
// Used to avoid performing unnecessary IPC when UpdateEverHitTelemetry is
|
|
// successively called without the cache-hit ratio changed.
|
|
uint32_t mLastEverHitRatio = NOT_YET_REPORTED;
|
|
|
|
// The number of times each cache entry is ever hit.
|
|
// Used with mEntryInserted to calculate the cache-hit ratio, for the
|
|
// dom.script_memory_cache_ever_hit telemetry.
|
|
size_t mEntryEverHit = 0;
|
|
|
|
// The number of times a new cache entry is inserted.
|
|
size_t mEntryInserted = 0;
|
|
|
|
// A map from content process ID to the cache-hit ratio, in [0,100] range.
|
|
// The parent process uses CONTENT_PROCESS_ID_MAIN as the ID.
|
|
//
|
|
// This is the telemetry data which is eventually be reflected to the
|
|
// dom.script_memory_cache_ever_hit telemetry probe.
|
|
//
|
|
// This field is used only on the parent process.
|
|
//
|
|
// Content processes periodically send the data to the parent process,
|
|
// and the parent process keeps the latest value, until the process shutdown.
|
|
using EverHitMapType = HashMap<uint64_t, uint32_t>;
|
|
UniquePtr<EverHitMapType> mEverHitMap;
|
|
|
|
Mutex mEncodeMutex{"SharedScriptCache::mEncodeMutex"};
|
|
Vector<EncodeItem> mEncodeItems MOZ_GUARDED_BY(mEncodeMutex);
|
|
|
|
nsCOMPtr<nsITimer> mDiskCacheTimer;
|
|
};
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_dom_SharedScriptCache_h
|