Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef MediaCache_h_
8 : #define MediaCache_h_
9 :
10 : #include "Intervals.h"
11 : #include "mozilla/UniquePtr.h"
12 : #include "nsCOMPtr.h"
13 : #include "nsHashKeys.h"
14 : #include "nsTArray.h"
15 : #include "nsTHashtable.h"
16 :
17 : class nsIPrincipal;
18 :
19 : namespace mozilla {
20 : // defined in MediaResource.h
21 : class ChannelMediaResource;
22 : typedef media::IntervalSet<int64_t> MediaByteRangeSet;
23 : class MediaResource;
24 : class ReentrantMonitorAutoEnter;
25 :
26 : /**
27 : * Media applications want fast, "on demand" random access to media data,
28 : * for pausing, seeking, etc. But we are primarily interested
29 : * in transporting media data using HTTP over the Internet, which has
30 : * high latency to open a connection, requires a new connection for every
31 : * seek, may not even support seeking on some connections (especially
32 : * live streams), and uses a push model --- data comes from the server
33 : * and you don't have much control over the rate. Also, transferring data
34 : * over the Internet can be slow and/or unpredictable, so we want to read
35 : * ahead to buffer and cache as much data as possible.
36 : *
37 : * The job of the media cache is to resolve this impedance mismatch.
38 : * The media cache reads data from Necko channels into file-backed storage,
39 : * and offers a random-access file-like API to the stream data
40 : * (MediaCacheStream). Along the way it solves several problems:
41 : * -- The cache intelligently reads ahead to prefetch data that may be
42 : * needed in the future
43 : * -- The size of the cache is bounded so that we don't fill up
44 : * storage with read-ahead data
45 : * -- Cache replacement is managed globally so that the most valuable
46 : * data (across all streams) is retained
47 : * -- The cache can suspend Necko channels temporarily when their data is
48 : * not wanted (yet)
49 : * -- The cache translates file-like seek requests to HTTP seeks,
50 : * including optimizations like not triggering a new seek if it would
51 : * be faster to just keep reading until we reach the seek point. The
52 : * "seek to EOF" idiom to determine file size is also handled efficiently
53 : * (seeking to EOF and then seeking back to the previous offset does not
54 : * trigger any Necko activity)
55 : * -- The cache also handles the case where the server does not support
56 : * seeking
57 : * -- Necko can only send data to the main thread, but MediaCacheStream
58 : * can distribute data to any thread
59 : * -- The cache exposes APIs so clients can detect what data is
60 : * currently held
61 : *
62 : * Note that although HTTP is the most important transport and we only
63 : * support transport-level seeking via HTTP byte-ranges, the media cache
64 : * works with any kind of Necko channels and provides random access to
65 : * cached data even for, e.g., FTP streams.
66 : *
67 : * The media cache is not persistent. It does not currently allow
68 : * data from one load to be used by other loads, either within the same
69 : * browser session or across browser sessions. The media cache file
70 : * is marked "delete on close" so it will automatically disappear in the
71 : * event of a browser crash or shutdown.
72 : *
73 : * The media cache is block-based. Streams are divided into blocks of a
74 : * fixed size (currently 4K) and we cache blocks. A single cache contains
75 : * blocks for all streams.
76 : *
77 : * The cache size is controlled by the media.cache_size preference
78 : * (which is in KB). The default size is 500MB.
79 : *
80 : * The replacement policy predicts a "time of next use" for each block
81 : * in the cache. When we need to free a block, the block with the latest
82 : * "time of next use" will be evicted. Blocks are divided into
83 : * different classes, each class having its own predictor:
84 : * FREE_BLOCK: these blocks are effectively infinitely far in the future;
85 : * a free block will always be chosen for replacement before other classes
86 : * of blocks.
87 : * METADATA_BLOCK: these are blocks that contain data that has been read
88 : * by the decoder in "metadata mode", e.g. while the decoder is searching
89 : * the stream during a seek operation. These blocks are managed with an
90 : * LRU policy; the "time of next use" is predicted to be as far in the
91 : * future as the last use was in the past.
92 : * PLAYED_BLOCK: these are blocks that have not been read in "metadata
93 : * mode", and contain data behind the current decoder read point. (They
94 : * may not actually have been read by the decoder, if the decoder seeked
95 : * forward.) These blocks are managed with an LRU policy except that we add
96 : * REPLAY_DELAY seconds of penalty to their predicted "time of next use",
97 : * to reflect the uncertainty about whether replay will actually happen
98 : * or not.
99 : * READAHEAD_BLOCK: these are blocks that have not been read in
100 : * "metadata mode" and that are entirely ahead of the current decoder
101 : * read point. (They may actually have been read by the decoder in the
102 : * past if the decoder has since seeked backward.) We predict the
103 : * time of next use for these blocks by assuming steady playback and
104 : * dividing the number of bytes between the block and the current decoder
105 : * read point by the decoder's estimate of its playback rate in bytes
106 : * per second. This ensures that the blocks farthest ahead are considered
107 : * least valuable.
108 : * For efficient prediction of the "latest time of next use", we maintain
109 : * linked lists of blocks in each class, ordering blocks by time of
110 : * next use. READAHEAD_BLOCKS have one linked list per stream, since their
111 : * time of next use depends on stream parameters, but the other lists
112 : * are global.
113 : *
114 : * A block containing a current decoder read point can contain data
115 : * both behind and ahead of the read point. It will be classified as a
116 : * PLAYED_BLOCK but we will give it special treatment so it is never
117 : * evicted --- it actually contains the highest-priority readahead data
118 : * as well as played data.
119 : *
120 : * "Time of next use" estimates are also used for flow control. When
121 : * reading ahead we can predict the time of next use for the data that
122 : * will be read. If the predicted time of next use is later then the
123 : * prediction for all currently cached blocks, and the cache is full, then
124 : * we should suspend reading from the Necko channel.
125 : *
126 : * Unfortunately suspending the Necko channel can't immediately stop the
127 : * flow of data from the server. First our desire to suspend has to be
128 : * transmitted to the server (in practice, Necko stops reading from the
129 : * socket, which causes the kernel to shrink its advertised TCP receive
130 : * window size to zero). Then the server can stop sending the data, but
131 : * we will receive data roughly corresponding to the product of the link
132 : * bandwidth multiplied by the round-trip latency. We deal with this by
133 : * letting the cache overflow temporarily and then trimming it back by
134 : * moving overflowing blocks back into the body of the cache, replacing
135 : * less valuable blocks as they become available. We try to avoid simply
136 : * discarding overflowing readahead data.
137 : *
138 : * All changes to the actual contents of the cache happen on the main
139 : * thread, since that's where Necko's notifications happen.
140 : *
141 : * The media cache maintains at most one Necko channel for each stream.
142 : * (In the future it might be advantageous to relax this, e.g. so that a
143 : * seek to near the end of the file can happen without disturbing
144 : * the loading of data from the beginning of the file.) The Necko channel
145 : * is managed through ChannelMediaResource; MediaCache does not
146 : * depend on Necko directly.
147 : *
148 : * Every time something changes that might affect whether we want to
149 : * read from a Necko channel, or whether we want to seek on the Necko
150 : * channel --- such as data arriving or data being consumed by the
151 : * decoder --- we asynchronously trigger MediaCache::Update on the main
152 : * thread. That method implements most cache policy. It evaluates for
153 : * each stream whether we want to suspend or resume the stream and what
154 : * offset we should seek to, if any. It is also responsible for trimming
155 : * back the cache size to its desired limit by moving overflowing blocks
156 : * into the main part of the cache.
157 : *
158 : * Streams can be opened in non-seekable mode. In non-seekable mode,
159 : * the cache will only call ChannelMediaResource::CacheClientSeek with
160 : * a 0 offset. The cache tries hard not to discard readahead data
161 : * for non-seekable streams, since that could trigger a potentially
162 : * disastrous re-read of the entire stream. It's up to cache clients
163 : * to try to avoid requesting seeks on such streams.
164 : *
165 : * MediaCache has a single internal monitor for all synchronization.
166 : * This is treated as the lowest level monitor in the media code. So,
167 : * we must not acquire any MediaDecoder locks or MediaResource locks
168 : * while holding the MediaCache lock. But it's OK to hold those locks
169 : * and then get the MediaCache lock.
170 : *
171 : * MediaCache associates a principal with each stream. CacheClientSeek
172 : * can trigger new HTTP requests; due to redirects to other domains,
173 : * each HTTP load can return data with a different principal. This
174 : * principal must be passed to NotifyDataReceived, and MediaCache
175 : * will detect when different principals are associated with data in the
176 : * same stream, and replace them with a null principal.
177 : */
178 : class MediaCache;
179 :
180 : /**
181 : * If the cache fails to initialize then Init will fail, so nonstatic
182 : * methods of this class can assume gMediaCache is non-null.
183 : *
184 : * This class can be directly embedded as a value.
185 : */
186 : class MediaCacheStream {
187 : public:
188 : // This needs to be a power of two
189 : static const int64_t BLOCK_SIZE = 32768;
190 :
191 : enum ReadMode {
192 : MODE_METADATA,
193 : MODE_PLAYBACK
194 : };
195 :
196 : // aClient provides the underlying transport that cache will use to read
197 : // data for this stream.
198 : MediaCacheStream(ChannelMediaResource* aClient, bool aIsPrivateBrowsing);
199 : ~MediaCacheStream();
200 :
201 : // Set up this stream with the cache. Can fail on OOM.
202 : // aContentLength is the content length if known, otherwise -1.
203 : // Exactly one of InitAsClone or Init must be called before any other method
204 : // on this class. Does nothing if already initialized.
205 : nsresult Init(int64_t aContentLength);
206 :
207 : // Set up this stream with the cache, assuming it's for the same data
208 : // as the aOriginal stream. Can fail on OOM.
209 : // Exactly one of InitAsClone or Init must be called before any other method
210 : // on this class. Does nothing if already initialized.
211 : nsresult InitAsClone(MediaCacheStream* aOriginal);
212 :
213 : // These are called on the main thread.
214 : // Tell us whether the stream is seekable or not. Non-seekable streams
215 : // will always pass 0 for aOffset to CacheClientSeek. This should only
216 : // be called while the stream is at channel offset 0. Seekability can
217 : // change during the lifetime of the MediaCacheStream --- every time
218 : // we do an HTTP load the seekability may be different (and sometimes
219 : // is, in practice, due to the effects of caching proxies).
220 : void SetTransportSeekable(bool aIsTransportSeekable);
221 : // This must be called (and return) before the ChannelMediaResource
222 : // used to create this MediaCacheStream is deleted.
223 : void Close();
224 : // This returns true when the stream has been closed
225 0 : bool IsClosed() const { return mClosed; }
226 : // Returns true when this stream is can be shared by a new resource load
227 0 : bool IsAvailableForSharing() const
228 : {
229 0 : return !mClosed && !mIsPrivateBrowsing &&
230 0 : (!mDidNotifyDataEnded || NS_SUCCEEDED(mNotifyDataEndedStatus));
231 : }
232 : // Get the principal for this stream. Anything accessing the contents of
233 : // this stream must have a principal that subsumes this principal.
234 0 : nsIPrincipal* GetCurrentPrincipal() { return mPrincipal; }
235 : // Ensure a global media cache update has run with this stream present.
236 : // This ensures the cache has had a chance to suspend or unsuspend this stream.
237 : // Called only on main thread. This can change the state of streams, fire
238 : // notifications, etc.
239 : void EnsureCacheUpdate();
240 :
241 : // These callbacks are called on the main thread by the client
242 : // when data has been received via the channel.
243 : // Tells the cache what the server said the data length is going to be.
244 : // The actual data length may be greater (we receive more data than
245 : // specified) or smaller (the stream ends before we reach the given
246 : // length), because servers can lie. The server's reported data length
247 : // *and* the actual data length can even vary over time because a
248 : // misbehaving server may feed us a different stream after each seek
249 : // operation. So this is really just a hint. The cache may however
250 : // stop reading (suspend the channel) when it thinks we've read all the
251 : // data available based on an incorrect reported length. Seeks relative
252 : // EOF also depend on the reported length if we haven't managed to
253 : // read the whole stream yet.
254 : void NotifyDataLength(int64_t aLength);
255 : // Notifies the cache that a load has begun. We pass the offset
256 : // because in some cases the offset might not be what the cache
257 : // requested. In particular we might unexpectedly start providing
258 : // data at offset 0. This need not be called if the offset is the
259 : // offset that the cache requested in
260 : // ChannelMediaResource::CacheClientSeek. This can be called at any
261 : // time by the client, not just after a CacheClientSeek.
262 : void NotifyDataStarted(int64_t aOffset);
263 : // Notifies the cache that data has been received. The stream already
264 : // knows the offset because data is received in sequence and
265 : // the starting offset is known via NotifyDataStarted or because
266 : // the cache requested the offset in
267 : // ChannelMediaResource::CacheClientSeek, or because it defaulted to 0.
268 : // We pass in the principal that was used to load this data.
269 : void NotifyDataReceived(int64_t aSize, const char* aData,
270 : nsIPrincipal* aPrincipal);
271 : // Notifies the cache that the current bytes should be written to disk.
272 : // Called on the main thread.
273 : void FlushPartialBlock();
274 : // Notifies the cache that the channel has closed with the given status.
275 : void NotifyDataEnded(nsresult aStatus);
276 :
277 : // Notifies the stream that the channel is reopened. The stream should
278 : // reset variables such as |mDidNotifyDataEnded|.
279 : void NotifyChannelRecreated();
280 :
281 : // These methods can be called on any thread.
282 : // Cached blocks associated with this stream will not be evicted
283 : // while the stream is pinned.
284 : void Pin();
285 : void Unpin();
286 : // See comments above for NotifyDataLength about how the length
287 : // can vary over time. Returns -1 if no length is known. Returns the
288 : // reported length if we haven't got any better information. If
289 : // the stream ended normally we return the length we actually got.
290 : // If we've successfully read data beyond the originally reported length,
291 : // we return the end of the data we've read.
292 : int64_t GetLength();
293 : // Returns the unique resource ID. Call only on the main thread or while
294 : // holding the media cache lock.
295 0 : int64_t GetResourceID() { return mResourceID; }
296 : // Returns the end of the bytes starting at the given offset
297 : // which are in cache.
298 : int64_t GetCachedDataEnd(int64_t aOffset);
299 : // Returns the offset of the first byte of cached data at or after aOffset,
300 : // or -1 if there is no such cached data.
301 : int64_t GetNextCachedData(int64_t aOffset);
302 : // Fills aRanges with the ByteRanges representing the data which is currently
303 : // cached. Locks the media cache while running, to prevent any ranges
304 : // growing. The stream should be pinned while this runs and while its results
305 : // are used, to ensure no data is evicted.
306 : nsresult GetCachedRanges(MediaByteRangeSet& aRanges);
307 :
308 : // Reads from buffered data only. Will fail if not all data to be read is
309 : // in the cache. Will not mark blocks as read. Can be called from the main
310 : // thread. It's the caller's responsibility to wrap the call in a pin/unpin,
311 : // and also to check that the range they want is cached before calling this.
312 : nsresult ReadFromCache(char* aBuffer,
313 : int64_t aOffset,
314 : int64_t aCount);
315 :
316 : // IsDataCachedToEndOfStream returns true if all the data from
317 : // aOffset to the end of the stream (the server-reported end, if the
318 : // real end is not known) is in cache. If we know nothing about the
319 : // end of the stream, this returns false.
320 : bool IsDataCachedToEndOfStream(int64_t aOffset);
321 : // The mode is initially MODE_PLAYBACK.
322 : void SetReadMode(ReadMode aMode);
323 : // This is the client's estimate of the playback rate assuming
324 : // the media plays continuously. The cache can't guess this itself
325 : // because it doesn't know when the decoder was paused, buffering, etc.
326 : // Do not pass zero.
327 : void SetPlaybackRate(uint32_t aBytesPerSecond);
328 : // Returns the last set value of SetTransportSeekable.
329 : bool IsTransportSeekable();
330 :
331 : // Returns true when all streams for this resource are suspended or their
332 : // channel has ended.
333 : bool AreAllStreamsForResourceSuspended();
334 :
335 : // These methods must be called on a different thread from the main
336 : // thread. They should always be called on the same thread for a given
337 : // stream.
338 : // This can fail when aWhence is NS_SEEK_END and no stream length
339 : // is known.
340 : nsresult Seek(int32_t aWhence, int64_t aOffset);
341 : int64_t Tell();
342 : // *aBytes gets the number of bytes that were actually read. This can
343 : // be less than aCount. If the first byte of data is not in the cache,
344 : // this will block until the data is available or the stream is
345 : // closed, otherwise it won't block.
346 : nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
347 : // Seeks to aOffset in the stream then performs a Read operation. See
348 : // 'Read' for argument and return details.
349 : nsresult ReadAt(int64_t aOffset, char* aBuffer,
350 : uint32_t aCount, uint32_t* aBytes);
351 :
352 : void ThrottleReadahead(bool bThrottle);
353 :
354 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
355 :
356 : private:
357 : friend class MediaCache;
358 :
359 : /**
360 : * A doubly-linked list of blocks. Add/Remove/Get methods are all
361 : * constant time. We declare this here so that a stream can contain a
362 : * BlockList of its read-ahead blocks. Blocks are referred to by index
363 : * into the MediaCache::mIndex array.
364 : *
365 : * Blocks can belong to more than one list at the same time, because
366 : * the next/prev pointers are not stored in the block.
367 : */
368 : class BlockList {
369 : public:
370 0 : BlockList() : mFirstBlock(-1), mCount(0) {}
371 0 : ~BlockList() {
372 0 : NS_ASSERTION(mFirstBlock == -1 && mCount == 0,
373 : "Destroying non-empty block list");
374 0 : }
375 : void AddFirstBlock(int32_t aBlock);
376 : void AddAfter(int32_t aBlock, int32_t aBefore);
377 : void RemoveBlock(int32_t aBlock);
378 : // Returns the first block in the list, or -1 if empty
379 0 : int32_t GetFirstBlock() const { return mFirstBlock; }
380 : // Returns the last block in the list, or -1 if empty
381 : int32_t GetLastBlock() const;
382 : // Returns the next block in the list after aBlock or -1 if
383 : // aBlock is the last block
384 : int32_t GetNextBlock(int32_t aBlock) const;
385 : // Returns the previous block in the list before aBlock or -1 if
386 : // aBlock is the first block
387 : int32_t GetPrevBlock(int32_t aBlock) const;
388 0 : bool IsEmpty() const { return mFirstBlock < 0; }
389 0 : int32_t GetCount() const { return mCount; }
390 : // The contents of aBlockIndex1 and aBlockIndex2 have been swapped
391 : void NotifyBlockSwapped(int32_t aBlockIndex1, int32_t aBlockIndex2);
392 : #ifdef DEBUG
393 : // Verify linked-list invariants
394 : void Verify();
395 : #else
396 : void Verify() {}
397 : #endif
398 :
399 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
400 :
401 : private:
402 0 : struct Entry : public nsUint32HashKey {
403 0 : explicit Entry(KeyTypePointer aKey) : nsUint32HashKey(aKey) { }
404 : Entry(const Entry& toCopy) : nsUint32HashKey(&toCopy.GetKey()),
405 : mNextBlock(toCopy.mNextBlock), mPrevBlock(toCopy.mPrevBlock) {}
406 :
407 : int32_t mNextBlock;
408 : int32_t mPrevBlock;
409 : };
410 : nsTHashtable<Entry> mEntries;
411 :
412 : // The index of the first block in the list, or -1 if the list is empty.
413 : int32_t mFirstBlock;
414 : // The number of blocks in the list.
415 : int32_t mCount;
416 : };
417 :
418 : // Returns the end of the bytes starting at the given offset
419 : // which are in cache.
420 : // This method assumes that the cache monitor is held and can be called on
421 : // any thread.
422 : int64_t GetCachedDataEndInternal(int64_t aOffset);
423 : // Returns the offset of the first byte of cached data at or after aOffset,
424 : // or -1 if there is no such cached data.
425 : // This method assumes that the cache monitor is held and can be called on
426 : // any thread.
427 : int64_t GetNextCachedDataInternal(int64_t aOffset);
428 : // Writes |mPartialBlock| to disk.
429 : // Used by |NotifyDataEnded| and |FlushPartialBlock|.
430 : // If |aNotifyAll| is true, this function will wake up readers who may be
431 : // waiting on the media cache monitor. Called on the main thread only.
432 : void FlushPartialBlockInternal(bool aNotify, ReentrantMonitorAutoEnter& aReentrantMonitor);
433 : // A helper function to do the work of closing the stream. Assumes
434 : // that the cache monitor is held. Main thread only.
435 : // aReentrantMonitor is the nsAutoReentrantMonitor wrapper holding the cache monitor.
436 : // This is used to NotifyAll to wake up threads that might be
437 : // blocked on reading from this stream.
438 : void CloseInternal(ReentrantMonitorAutoEnter& aReentrantMonitor);
439 : // Update mPrincipal given that data has been received from aPrincipal
440 : bool UpdatePrincipal(nsIPrincipal* aPrincipal);
441 :
442 : // Instance of MediaCache to use with this MediaCacheStream.
443 : RefPtr<MediaCache> mMediaCache;
444 :
445 : // These fields are main-thread-only.
446 : ChannelMediaResource* mClient;
447 : nsCOMPtr<nsIPrincipal> mPrincipal;
448 : // Set to true when MediaCache::Update() has finished while this stream
449 : // was present.
450 : bool mHasHadUpdate;
451 : // Set to true when the stream has been closed either explicitly or
452 : // due to an internal cache error
453 : bool mClosed;
454 : // True if CacheClientNotifyDataEnded has been called for this stream.
455 : bool mDidNotifyDataEnded;
456 :
457 : // The following fields must be written holding the cache's monitor and
458 : // only on the main thread, thus can be read either on the main thread
459 : // or while holding the cache's monitor.
460 :
461 : // This is a unique ID representing the resource we're loading.
462 : // All streams with the same mResourceID are loading the same
463 : // underlying resource and should share data.
464 : int64_t mResourceID;
465 : // The last reported seekability state for the underlying channel
466 : bool mIsTransportSeekable;
467 : // True if the cache has suspended our channel because the cache is
468 : // full and the priority of the data that would be received is lower
469 : // than the priority of the data already in the cache
470 : bool mCacheSuspended;
471 : // True if the channel ended and we haven't seeked it again.
472 : bool mChannelEnded;
473 : // The offset where the next data from the channel will arrive
474 : int64_t mChannelOffset;
475 : // The reported or discovered length of the data, or -1 if nothing is
476 : // known
477 : int64_t mStreamLength;
478 :
479 : // The following fields are protected by the cache's monitor can can be written
480 : // by any thread.
481 :
482 : // The offset where the reader is positioned in the stream
483 : int64_t mStreamOffset;
484 : // For each block in the stream data, maps to the cache entry for the
485 : // block, or -1 if the block is not cached.
486 : nsTArray<int32_t> mBlocks;
487 : // The list of read-ahead blocks, ordered by stream offset; the first
488 : // block is the earliest in the stream (so the last block will be the
489 : // least valuable).
490 : BlockList mReadaheadBlocks;
491 : // The list of metadata blocks; the first block is the most recently used
492 : BlockList mMetadataBlocks;
493 : // The list of played-back blocks; the first block is the most recently used
494 : BlockList mPlayedBlocks;
495 : // The last reported estimate of the decoder's playback rate
496 : uint32_t mPlaybackBytesPerSecond;
497 : // The number of times this stream has been Pinned without a
498 : // corresponding Unpin
499 : uint32_t mPinCount;
500 : // The status used when we did CacheClientNotifyDataEnded. Only valid
501 : // when mDidNotifyDataEnded is true.
502 : nsresult mNotifyDataEndedStatus;
503 : // The last reported read mode
504 : ReadMode mCurrentMode;
505 : // True if some data in mPartialBlockBuffer has been read as metadata
506 : bool mMetadataInPartialBlockBuffer;
507 :
508 : // The following field is protected by the cache's monitor but are
509 : // only written on the main thread.
510 :
511 : // Data received for the block containing mChannelOffset. Data needs
512 : // to wait here so we can write back a complete block. The first
513 : // mChannelOffset%BLOCK_SIZE bytes have been filled in with good data,
514 : // the rest are garbage.
515 : // Heap allocate this buffer since the exact power-of-2 will cause allocation
516 : // slop when combined with the rest of the object members.
517 : UniquePtr<uint8_t[]> mPartialBlockBuffer = MakeUnique<uint8_t[]>(BLOCK_SIZE);
518 :
519 : // True if associated with a private browsing window.
520 : const bool mIsPrivateBrowsing;
521 :
522 : bool mThrottleReadahead = false;
523 : };
524 :
525 : } // namespace mozilla
526 :
527 : #endif
|