tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

URLPreloader.h (10070B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #ifndef URLPreloader_h
      7 #define URLPreloader_h
      8 
      9 #include "mozilla/DataMutex.h"
     10 #include "mozilla/FileLocation.h"
     11 #include "mozilla/HashFunctions.h"
     12 #include "mozilla/LinkedList.h"
     13 #include "mozilla/MemoryReporting.h"
     14 #include "mozilla/Monitor.h"
     15 #include "mozilla/Omnijar.h"
     16 #include "mozilla/Result.h"
     17 #include "nsClassHashtable.h"
     18 #include "nsHashKeys.h"
     19 #include "nsIChromeRegistry.h"
     20 #include "nsIFile.h"
     21 #include "nsIURI.h"
     22 #include "nsIMemoryReporter.h"
     23 #include "nsIResProtocolHandler.h"
     24 #include "nsIThread.h"
     25 #include "nsReadableUtils.h"
     26 
     27 class nsZipArchive;
     28 
     29 namespace mozilla {
     30 namespace loader {
     31 class InputBuffer;
     32 }
     33 
     34 using namespace mozilla::loader;
     35 
     36 class ScriptPreloader;
     37 
     38 /**
     39 * A singleton class to manage loading local URLs during startup, recording
     40 * them, and pre-loading them during early startup in the next session. URLs
     41 * that are not already loaded (or already being pre-loaded) when required are
     42 * read synchronously from disk, and (if startup is not already complete)
     43 * added to the pre-load list for the next session.
     44 */
     45 class URLPreloader final : public nsIMemoryReporter {
     46  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
     47 
     48  URLPreloader() = default;
     49 
     50 public:
     51  NS_DECL_THREADSAFE_ISUPPORTS
     52  NS_DECL_NSIMEMORYREPORTER
     53 
     54  static URLPreloader& GetSingleton();
     55 
     56  // The type of read operation to perform.
     57  enum ReadType {
     58    // Read the file and then immediately forget its data.
     59    Forget,
     60    // Read the file and retain its data for the next caller.
     61    Retain,
     62  };
     63 
     64  // Helpers to read the contents of files or JAR archive entries with various
     65  // representations. If the preloader has not yet been initialized, or the
     66  // given location is not supported by the cache, the entries will be read
     67  // synchronously, and not stored in the cache.
     68  static Result<nsCString, nsresult> Read(FileLocation& location,
     69                                          ReadType readType = Forget);
     70 
     71  static Result<nsCString, nsresult> ReadURI(nsIURI* uri,
     72                                             ReadType readType = Forget);
     73 
     74  static Result<nsCString, nsresult> ReadFile(nsIFile* file,
     75                                              ReadType readType = Forget);
     76 
     77  static Result<nsCString, nsresult> ReadZip(nsZipArchive* archive,
     78                                             const nsACString& path,
     79                                             ReadType readType = Forget);
     80 
     81  void SetStartupFinished() { mStartupFinished = true; }
     82 
     83 private:
     84  struct CacheKey;
     85 
     86  Result<nsCString, nsresult> ReadInternal(const CacheKey& key,
     87                                           ReadType readType);
     88 
     89  Result<nsCString, nsresult> ReadURIInternal(nsIURI* uri, ReadType readType);
     90 
     91  Result<nsCString, nsresult> ReadFileInternal(nsIFile* file,
     92                                               ReadType readType);
     93 
     94  static Result<nsCString, nsresult> Read(const CacheKey& key,
     95                                          ReadType readType);
     96 
     97  static bool sInitialized;
     98 
     99  static mozilla::StaticRefPtr<URLPreloader> sSingleton;
    100 
    101 protected:
    102  friend class AddonManagerStartup;
    103  friend class ScriptPreloader;
    104 
    105  virtual ~URLPreloader();
    106 
    107  Result<Ok, nsresult> WriteCache();
    108 
    109  static URLPreloader& ReInitialize();
    110 
    111  // Clear leftover entries after the cache has been written.
    112  void Cleanup();
    113 
    114  // Begins reading files off-thread, and ensures that initialization has
    115  // completed before leaving the current scope. The caller *must* ensure that
    116  // no code on the main thread access Omnijar, either directly or indirectly,
    117  // for the lifetime of this guard object.
    118  struct MOZ_RAII AutoBeginReading final {
    119    AutoBeginReading() { GetSingleton().BeginBackgroundRead(); }
    120 
    121    ~AutoBeginReading() {
    122      auto& reader = GetSingleton();
    123 
    124      MonitorAutoLock mal(reader.mMonitor);
    125 
    126      while (!reader.mReaderInitialized && URLPreloader::sInitialized) {
    127        mal.Wait();
    128      }
    129    }
    130  };
    131 
    132 private:
    133  // Represents a key for an entry in the URI cache, based on its file or JAR
    134  // location.
    135  struct CacheKey {
    136    // The type of the entry. TypeAppJar and TypeGREJar entries are in the
    137    // app-specific or toolkit Omnijar files, and are handled specially.
    138    // TypeFile entries are plain files in the filesystem.
    139    enum EntryType : uint8_t {
    140      TypeAppJar,
    141      TypeGREJar,
    142      TypeFile,
    143    };
    144 
    145    CacheKey() = default;
    146    CacheKey(const CacheKey& other) = default;
    147 
    148    CacheKey(EntryType type, const nsACString& path)
    149        : mType(type), mPath(path) {}
    150 
    151    explicit CacheKey(nsIFile* file) : mType(TypeFile) {
    152      nsString path;
    153      MOZ_ALWAYS_SUCCEEDS(file->GetPath(path));
    154      MOZ_DIAGNOSTIC_ASSERT(path.Length() > 0);
    155      CopyUTF16toUTF8(path, mPath);
    156    }
    157 
    158    explicit inline CacheKey(InputBuffer& buffer);
    159 
    160    // Encodes or decodes the cache key for storage in a session cache file.
    161    template <typename Buffer>
    162    void Code(Buffer& buffer) {
    163      buffer.codeUint8(*reinterpret_cast<uint8_t*>(&mType));
    164      buffer.codeString(mPath);
    165      MOZ_DIAGNOSTIC_ASSERT(mPath.Length() > 0);
    166    }
    167 
    168    uint32_t Hash() const { return HashGeneric(mType, HashString(mPath)); }
    169 
    170    bool operator==(const CacheKey& other) const {
    171      return mType == other.mType && mPath == other.mPath;
    172    }
    173 
    174    // Returns the Omnijar type for this entry. This may *only* be called
    175    // for Omnijar entries.
    176    Omnijar::Type OmnijarType() {
    177      switch (mType) {
    178        case TypeAppJar:
    179          return Omnijar::APP;
    180        case TypeGREJar:
    181          return Omnijar::GRE;
    182        default:
    183          MOZ_CRASH("Unexpected entry type");
    184          return Omnijar::GRE;
    185      }
    186    }
    187 
    188    const char* TypeString() const {
    189      switch (mType) {
    190        case TypeAppJar:
    191          return "AppJar";
    192        case TypeGREJar:
    193          return "GREJar";
    194        case TypeFile:
    195          return "File";
    196      }
    197      MOZ_ASSERT_UNREACHABLE("no such type");
    198      return "";
    199    }
    200 
    201    already_AddRefed<nsZipArchive> Archive() {
    202      return Omnijar::GetReader(OmnijarType());
    203    }
    204 
    205    Result<FileLocation, nsresult> ToFileLocation();
    206 
    207    EntryType mType = TypeFile;
    208 
    209    // The path of the entry. For Type*Jar entries, this is the path within
    210    // the Omnijar archive. For TypeFile entries, this is the full path to
    211    // the file.
    212    nsCString mPath{};
    213  };
    214 
    215  // Represents an entry in the URI cache.
    216  struct URLEntry final : public CacheKey, public LinkedListElement<URLEntry> {
    217    MOZ_IMPLICIT URLEntry(const CacheKey& key)
    218        : CacheKey(key), mData(VoidCString()) {}
    219 
    220    explicit URLEntry(nsIFile* file) : CacheKey(file) {}
    221 
    222    // For use with nsTArray::Sort.
    223    //
    224    // Sorts entries by the time they were initially read during this
    225    // session.
    226    struct Comparator final {
    227      bool Equals(const URLEntry* a, const URLEntry* b) const {
    228        return a->mReadTime == b->mReadTime;
    229      }
    230 
    231      bool LessThan(const URLEntry* a, const URLEntry* b) const {
    232        return a->mReadTime < b->mReadTime;
    233      }
    234    };
    235 
    236    // Sets the first-used time of this file to the earlier of its current
    237    // first-use time or the given timestamp.
    238    void UpdateUsedTime(const TimeStamp& time = TimeStamp::Now()) {
    239      if (!mReadTime || time < mReadTime) {
    240        mReadTime = time;
    241      }
    242    }
    243 
    244    Result<nsCString, nsresult> Read();
    245    static Result<nsCString, nsresult> ReadLocation(FileLocation& location);
    246 
    247    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    248      return (mallocSizeOf(this) +
    249              mPath.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
    250              mData.SizeOfExcludingThisEvenIfShared(mallocSizeOf));
    251    }
    252 
    253    // Reads the contents of the file referenced by this entry, or wait for
    254    // an off-thread read operation to finish if it is currently pending,
    255    // and return the file's contents.
    256    Result<nsCString, nsresult> ReadOrWait(ReadType readType);
    257 
    258    nsCString mData;
    259 
    260    TimeStamp mReadTime{};
    261 
    262    nsresult mResultCode = NS_OK;
    263  };
    264 
    265  // Resolves the given URI to a CacheKey, if the URI is cacheable.
    266  Result<CacheKey, nsresult> ResolveURI(nsIURI* uri);
    267 
    268  static already_AddRefed<URLPreloader> Create(bool* aInitialized);
    269 
    270  Result<Ok, nsresult> InitInternal();
    271 
    272  // Returns a file pointer to the (possibly nonexistent) cache file with the
    273  // given suffix.
    274  Result<nsCOMPtr<nsIFile>, nsresult> GetCacheFile(const nsAString& suffix);
    275  // Finds the correct cache file to use for this session.
    276  Result<nsCOMPtr<nsIFile>, nsresult> FindCacheFile();
    277 
    278  Result<Ok, nsresult> ReadCache(LinkedList<URLEntry>& pendingURLs);
    279 
    280  void BackgroundReadFiles();
    281  void BeginBackgroundRead();
    282 
    283  using HashType = nsClassHashtable<nsGenericHashKey<CacheKey>, URLEntry>;
    284 
    285  size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
    286 
    287  bool mStartupFinished = false;
    288  bool mReaderInitialized = false;
    289 
    290  // Only to be accessed from the cache write thread.
    291  bool mCacheWritten = false;
    292 
    293  // The prefix URLs for files in the GRE and App omni jar archives.
    294  nsCString mGREPrefix;
    295  nsCString mAppPrefix;
    296 
    297  nsCOMPtr<nsIResProtocolHandler> mResProto;
    298  nsCOMPtr<nsIChromeRegistry> mChromeReg;
    299  nsCOMPtr<nsIFile> mProfD;
    300 
    301  // Note: We use a RefPtr rather than an nsCOMPtr here because the
    302  // AssertNoQueryNeeded checks done by getter_AddRefs happen at a time that
    303  // violate data access invariants. It's wrapped in a mutex because
    304  // the reader thread needs to be able to null this out to terminate itself.
    305  DataMutex<RefPtr<nsIThread>> mReaderThread{"ReaderThread"};
    306 
    307  // A map of URL entries which have were either read this session, or read
    308  // from the last session's cache file.
    309  HashType mCachedURLs;
    310 
    311  Monitor mMonitor MOZ_UNANNOTATED{"[URLPreloader::mMutex]"};
    312 };
    313 
    314 }  // namespace mozilla
    315 
    316 #endif  // URLPreloader_h