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/FileLocation.h" 10 #include "mozilla/HashFunctions.h" 11 #include "mozilla/LinkedList.h" 12 #include "mozilla/MemoryReporting.h" 13 #include "mozilla/Monitor.h" 14 #include "mozilla/Omnijar.h" 15 #include "mozilla/Range.h" 16 #include "mozilla/Vector.h" 17 #include "mozilla/Result.h" 18 #include "nsClassHashtable.h" 19 #include "nsHashKeys.h" 20 #include "nsIChromeRegistry.h" 21 #include "nsIFile.h" 22 #include "nsIURI.h" 23 #include "nsIMemoryReporter.h" 24 #include "nsIObserver.h" 25 #include "nsIResProtocolHandler.h" 26 #include "nsIThread.h" 27 #include "nsReadableUtils.h" 28 29 class nsZipArchive; 30 31 namespace mozilla { 32 namespace loader { 33 class InputBuffer; 34 } 35 36 using namespace mozilla::loader; 37 38 class ScriptPreloader; 39 40 /** 41 * A singleton class to manage loading local URLs during startup, recording 42 * them, and pre-loading them during early startup in the next session. URLs 43 * that are not already loaded (or already being pre-loaded) when required are 44 * read synchronously from disk, and (if startup is not already complete) 45 * added to the pre-load list for the next session. 46 */ 47 class URLPreloader final : public nsIObserver, public nsIMemoryReporter { 48 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) 49 50 URLPreloader(); 51 52 public: 53 NS_DECL_THREADSAFE_ISUPPORTS 54 NS_DECL_NSIOBSERVER 55 NS_DECL_NSIMEMORYREPORTER 56 57 static URLPreloader& GetSingleton(); 58 59 // The type of read operation to perform. 60 enum ReadType { 61 // Read the file and then immediately forget its data. 62 Forget, 63 // Read the file and retain its data for the next caller. 64 Retain, 65 }; 66 67 // Helpers to read the contents of files or JAR archive entries with various 68 // representations. If the preloader has not yet been initialized, or the 69 // given location is not supported by the cache, the entries will be read 70 // synchronously, and not stored in the cache. 71 static Result<const nsCString, nsresult> Read(FileLocation& location, 72 ReadType readType = Forget); 73 74 static Result<const nsCString, nsresult> ReadURI(nsIURI* uri, 75 ReadType readType = Forget); 76 77 static Result<const nsCString, nsresult> ReadFile(nsIFile* file, 78 ReadType readType = Forget); 79 80 static Result<const nsCString, nsresult> ReadZip(nsZipArchive* archive, 81 const nsACString& path, 82 ReadType readType = Forget); 83 84 private: 85 struct CacheKey; 86 87 Result<const nsCString, nsresult> ReadInternal(const CacheKey& key, 88 ReadType readType); 89 90 Result<const nsCString, nsresult> ReadURIInternal(nsIURI* uri, 91 ReadType readType); 92 93 Result<const nsCString, nsresult> ReadFileInternal(nsIFile* file, 94 ReadType readType); 95 96 static Result<const nsCString, nsresult> Read(const CacheKey& key, 97 ReadType readType); 98 99 static bool sInitialized; 100 101 static mozilla::StaticRefPtr<URLPreloader> sSingleton; 102 103 protected: 104 friend class AddonManagerStartup; 105 friend class ScriptPreloader; 106 107 virtual ~URLPreloader(); 108 109 Result<Ok, nsresult> WriteCache(); 110 111 static URLPreloader& ReInitialize(); 112 113 // Clear leftover entries after the cache has been written. 114 void Cleanup(); 115 116 // Begins reading files off-thread, and ensures that initialization has 117 // completed before leaving the current scope. The caller *must* ensure that 118 // no code on the main thread access Omnijar, either directly or indirectly, 119 // for the lifetime of this guard object. 120 struct MOZ_RAII AutoBeginReading final { AutoBeginReadingfinal121 AutoBeginReading() { GetSingleton().BeginBackgroundRead(); } 122 ~AutoBeginReadingfinal123 ~AutoBeginReading() { 124 auto& reader = GetSingleton(); 125 126 MonitorAutoLock mal(reader.mMonitor); 127 128 while (!reader.mReaderInitialized && URLPreloader::sInitialized) { 129 mal.Wait(); 130 } 131 } 132 }; 133 134 private: 135 // Represents a key for an entry in the URI cache, based on its file or JAR 136 // location. 137 struct CacheKey { 138 // The type of the entry. TypeAppJar and TypeGREJar entries are in the 139 // app-specific or toolkit Omnijar files, and are handled specially. 140 // TypeFile entries are plain files in the filesystem. 141 enum EntryType : uint8_t { 142 TypeAppJar, 143 TypeGREJar, 144 TypeFile, 145 }; 146 147 CacheKey() = default; 148 CacheKey(const CacheKey& other) = default; 149 CacheKeyCacheKey150 CacheKey(EntryType type, const nsACString& path) 151 : mType(type), mPath(path) {} 152 CacheKeyCacheKey153 explicit CacheKey(nsIFile* file) : mType(TypeFile) { 154 nsString path; 155 MOZ_ALWAYS_SUCCEEDS(file->GetPath(path)); 156 CopyUTF16toUTF8(path, mPath); 157 } 158 159 explicit inline CacheKey(InputBuffer& buffer); 160 161 // Encodes or decodes the cache key for storage in a session cache file. 162 template <typename Buffer> CodeCacheKey163 void Code(Buffer& buffer) { 164 buffer.codeUint8(*reinterpret_cast<uint8_t*>(&mType)); 165 buffer.codeString(mPath); 166 } 167 HashCacheKey168 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. OmnijarTypeCacheKey176 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 TypeStringCacheKey188 const char* TypeString() { 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 ArchiveCacheKey201 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> { URLEntryfinal217 MOZ_IMPLICIT URLEntry(const CacheKey& key) 218 : CacheKey(key), mData(VoidCString()) {} 219 URLEntryfinal220 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 { Equalsfinal::final227 bool Equals(const URLEntry* a, const URLEntry* b) const { 228 return a->mReadTime == b->mReadTime; 229 } 230 LessThanfinal::final231 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<const nsCString, nsresult> Read(); 245 static Result<const nsCString, nsresult> ReadLocation( 246 FileLocation& location); 247 SizeOfIncludingThisfinal248 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 249 return (mallocSizeOf(this) + 250 mPath.SizeOfExcludingThisEvenIfShared(mallocSizeOf) + 251 mData.SizeOfExcludingThisEvenIfShared(mallocSizeOf)); 252 } 253 254 // Reads the contents of the file referenced by this entry, or wait for 255 // an off-thread read operation to finish if it is currently pending, 256 // and return the file's contents. 257 Result<const nsCString, nsresult> ReadOrWait(ReadType readType); 258 259 nsCString mData; 260 261 TimeStamp mReadTime{}; 262 263 nsresult mResultCode = NS_OK; 264 }; 265 266 // Resolves the given URI to a CacheKey, if the URI is cacheable. 267 Result<CacheKey, nsresult> ResolveURI(nsIURI* uri); 268 269 Result<Ok, nsresult> InitInternal(); 270 271 // Returns a file pointer to the (possibly nonexistent) cache file with the 272 // given suffix. 273 Result<nsCOMPtr<nsIFile>, nsresult> GetCacheFile(const nsAString& suffix); 274 // Finds the correct cache file to use for this session. 275 Result<nsCOMPtr<nsIFile>, nsresult> FindCacheFile(); 276 277 Result<Ok, nsresult> ReadCache(LinkedList<URLEntry>& pendingURLs); 278 279 void BackgroundReadFiles(); 280 void BeginBackgroundRead(); 281 282 using HashType = nsClassHashtable<nsGenericHashKey<CacheKey>, URLEntry>; 283 284 size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); 285 286 bool mStartupFinished = false; 287 bool mReaderInitialized = false; 288 289 // Only to be accessed from the cache write thread. 290 bool mCacheWritten = false; 291 292 // The prefix URLs for files in the GRE and App omni jar archives. 293 nsCString mGREPrefix; 294 nsCString mAppPrefix; 295 296 nsCOMPtr<nsIResProtocolHandler> mResProto; 297 nsCOMPtr<nsIChromeRegistry> mChromeReg; 298 nsCOMPtr<nsIFile> mProfD; 299 300 // Note: We use a RefPtr rather than an nsCOMPtr here because the 301 // AssertNoQueryNeeded checks done by getter_AddRefs happen at a time that 302 // violate data access invariants. 303 RefPtr<nsIThread> mReaderThread; 304 305 // A map of URL entries which have were either read this session, or read 306 // from the last session's cache file. 307 HashType mCachedURLs; 308 309 Monitor mMonitor{"[URLPreloader::mMutex]"}; 310 }; 311 312 } // namespace mozilla 313 314 #endif // URLPreloader_h 315