Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* 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 URLPreloader_h
#define URLPreloader_h
#include "mozilla/DataMutex.h"
#include "mozilla/FileLocation.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Monitor.h"
#include "mozilla/Omnijar.h"
#include "mozilla/Range.h"
#include "mozilla/Vector.h"
#include "mozilla/Result.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsIChromeRegistry.h"
#include "nsIFile.h"
#include "nsIURI.h"
#include "nsIMemoryReporter.h"
#include "nsIResProtocolHandler.h"
#include "nsIThread.h"
#include "nsReadableUtils.h"
class nsZipArchive;
namespace mozilla {
namespace loader {
class InputBuffer;
}
using namespace mozilla::loader;
class ScriptPreloader;
/**
* A singleton class to manage loading local URLs during startup, recording
* them, and pre-loading them during early startup in the next session. URLs
* that are not already loaded (or already being pre-loaded) when required are
* read synchronously from disk, and (if startup is not already complete)
* added to the pre-load list for the next session.
*/
class URLPreloader final : public nsIMemoryReporter {
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
URLPreloader() = default;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
static URLPreloader& GetSingleton();
// The type of read operation to perform.
enum ReadType {
// Read the file and then immediately forget its data.
Forget,
// Read the file and retain its data for the next caller.
Retain,
};
// Helpers to read the contents of files or JAR archive entries with various
// representations. If the preloader has not yet been initialized, or the
// given location is not supported by the cache, the entries will be read
// synchronously, and not stored in the cache.
static Result<nsCString, nsresult> Read(FileLocation& location,
ReadType readType = Forget);
static Result<nsCString, nsresult> ReadURI(nsIURI* uri,
ReadType readType = Forget);
static Result<nsCString, nsresult> ReadFile(nsIFile* file,
ReadType readType = Forget);
static Result<nsCString, nsresult> ReadZip(nsZipArchive* archive,
const nsACString& path,
ReadType readType = Forget);
void SetStartupFinished() { mStartupFinished = true; }
private:
struct CacheKey;
Result<nsCString, nsresult> ReadInternal(const CacheKey& key,
ReadType readType);
Result<nsCString, nsresult> ReadURIInternal(nsIURI* uri, ReadType readType);
Result<nsCString, nsresult> ReadFileInternal(nsIFile* file,
ReadType readType);
static Result<nsCString, nsresult> Read(const CacheKey& key,
ReadType readType);
static bool sInitialized;
static mozilla::StaticRefPtr<URLPreloader> sSingleton;
protected:
friend class AddonManagerStartup;
friend class ScriptPreloader;
virtual ~URLPreloader();
Result<Ok, nsresult> WriteCache();
static URLPreloader& ReInitialize();
// Clear leftover entries after the cache has been written.
void Cleanup();
// Begins reading files off-thread, and ensures that initialization has
// completed before leaving the current scope. The caller *must* ensure that
// no code on the main thread access Omnijar, either directly or indirectly,
// for the lifetime of this guard object.
struct MOZ_RAII AutoBeginReading final {
AutoBeginReading() { GetSingleton().BeginBackgroundRead(); }
~AutoBeginReading() {
auto& reader = GetSingleton();
MonitorAutoLock mal(reader.mMonitor);
while (!reader.mReaderInitialized && URLPreloader::sInitialized) {
mal.Wait();
}
}
};
private:
// Represents a key for an entry in the URI cache, based on its file or JAR
// location.
struct CacheKey {
// The type of the entry. TypeAppJar and TypeGREJar entries are in the
// app-specific or toolkit Omnijar files, and are handled specially.
// TypeFile entries are plain files in the filesystem.
enum EntryType : uint8_t {
TypeAppJar,
TypeGREJar,
TypeFile,
};
CacheKey() = default;
CacheKey(const CacheKey& other) = default;
CacheKey(EntryType type, const nsACString& path)
: mType(type), mPath(path) {}
explicit CacheKey(nsIFile* file) : mType(TypeFile) {
nsString path;
MOZ_ALWAYS_SUCCEEDS(file->GetPath(path));
MOZ_DIAGNOSTIC_ASSERT(path.Length() > 0);
CopyUTF16toUTF8(path, mPath);
}
explicit inline CacheKey(InputBuffer& buffer);
// Encodes or decodes the cache key for storage in a session cache file.
template <typename Buffer>
void Code(Buffer& buffer) {
buffer.codeUint8(*reinterpret_cast<uint8_t*>(&mType));
buffer.codeString(mPath);
MOZ_DIAGNOSTIC_ASSERT(mPath.Length() > 0);
}
uint32_t Hash() const { return HashGeneric(mType, HashString(mPath)); }
bool operator==(const CacheKey& other) const {
return mType == other.mType && mPath == other.mPath;
}
// Returns the Omnijar type for this entry. This may *only* be called
// for Omnijar entries.
Omnijar::Type OmnijarType() {
switch (mType) {
case TypeAppJar:
return Omnijar::APP;
case TypeGREJar:
return Omnijar::GRE;
default:
MOZ_CRASH("Unexpected entry type");
return Omnijar::GRE;
}
}
const char* TypeString() const {
switch (mType) {
case TypeAppJar:
return "AppJar";
case TypeGREJar:
return "GREJar";
case TypeFile:
return "File";
}
MOZ_ASSERT_UNREACHABLE("no such type");
return "";
}
already_AddRefed<nsZipArchive> Archive() {
return Omnijar::GetReader(OmnijarType());
}
Result<FileLocation, nsresult> ToFileLocation();
EntryType mType = TypeFile;
// The path of the entry. For Type*Jar entries, this is the path within
// the Omnijar archive. For TypeFile entries, this is the full path to
// the file.
nsCString mPath{};
};
// Represents an entry in the URI cache.
struct URLEntry final : public CacheKey, public LinkedListElement<URLEntry> {
MOZ_IMPLICIT URLEntry(const CacheKey& key)
: CacheKey(key), mData(VoidCString()) {}
explicit URLEntry(nsIFile* file) : CacheKey(file) {}
// For use with nsTArray::Sort.
//
// Sorts entries by the time they were initially read during this
// session.
struct Comparator final {
bool Equals(const URLEntry* a, const URLEntry* b) const {
return a->mReadTime == b->mReadTime;
}
bool LessThan(const URLEntry* a, const URLEntry* b) const {
return a->mReadTime < b->mReadTime;
}
};
// Sets the first-used time of this file to the earlier of its current
// first-use time or the given timestamp.
void UpdateUsedTime(const TimeStamp& time = TimeStamp::Now()) {
if (!mReadTime || time < mReadTime) {
mReadTime = time;
}
}
Result<nsCString, nsresult> Read();
static Result<nsCString, nsresult> ReadLocation(FileLocation& location);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return (mallocSizeOf(this) +
mPath.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
mData.SizeOfExcludingThisEvenIfShared(mallocSizeOf));
}
// Reads the contents of the file referenced by this entry, or wait for
// an off-thread read operation to finish if it is currently pending,
// and return the file's contents.
Result<nsCString, nsresult> ReadOrWait(ReadType readType);
nsCString mData;
TimeStamp mReadTime{};
nsresult mResultCode = NS_OK;
};
// Resolves the given URI to a CacheKey, if the URI is cacheable.
Result<CacheKey, nsresult> ResolveURI(nsIURI* uri);
static already_AddRefed<URLPreloader> Create(bool* aInitialized);
Result<Ok, nsresult> InitInternal();
// Returns a file pointer to the (possibly nonexistent) cache file with the
// given suffix.
Result<nsCOMPtr<nsIFile>, nsresult> GetCacheFile(const nsAString& suffix);
// Finds the correct cache file to use for this session.
Result<nsCOMPtr<nsIFile>, nsresult> FindCacheFile();
Result<Ok, nsresult> ReadCache(LinkedList<URLEntry>& pendingURLs);
void BackgroundReadFiles();
void BeginBackgroundRead();
using HashType = nsClassHashtable<nsGenericHashKey<CacheKey>, URLEntry>;
size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
bool mStartupFinished = false;
bool mReaderInitialized = false;
// Only to be accessed from the cache write thread.
bool mCacheWritten = false;
// The prefix URLs for files in the GRE and App omni jar archives.
nsCString mGREPrefix;
nsCString mAppPrefix;
nsCOMPtr<nsIResProtocolHandler> mResProto;
nsCOMPtr<nsIChromeRegistry> mChromeReg;
nsCOMPtr<nsIFile> mProfD;
// Note: We use a RefPtr rather than an nsCOMPtr here because the
// AssertNoQueryNeeded checks done by getter_AddRefs happen at a time that
// violate data access invariants. It's wrapped in a mutex because
// the reader thread needs to be able to null this out to terminate itself.
DataMutex<RefPtr<nsIThread>> mReaderThread{"ReaderThread"};
// A map of URL entries which have were either read this session, or read
// from the last session's cache file.
HashType mCachedURLs;
Monitor mMonitor MOZ_UNANNOTATED{"[URLPreloader::mMutex]"};
};
} // namespace mozilla
#endif // URLPreloader_h