summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/loader/URLPreloader.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/loader/URLPreloader.h')
-rw-r--r--js/xpconnect/loader/URLPreloader.h318
1 files changed, 318 insertions, 0 deletions
diff --git a/js/xpconnect/loader/URLPreloader.h b/js/xpconnect/loader/URLPreloader.h
new file mode 100644
index 0000000000..2573fc89a2
--- /dev/null
+++ b/js/xpconnect/loader/URLPreloader.h
@@ -0,0 +1,318 @@
+/* -*- 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