1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef MEDIA_BLINK_URL_INDEX_H_
6 #define MEDIA_BLINK_URL_INDEX_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <map>
12 #include <vector>
13 
14 #include "base/macros.h"
15 #include "base/memory/memory_pressure_listener.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/threading/thread_checker.h"
19 #include "media/blink/lru.h"
20 #include "media/blink/media_blink_export.h"
21 #include "media/blink/multibuffer.h"
22 #include "services/network/public/mojom/fetch_api.mojom.h"
23 #include "url/gurl.h"
24 
25 namespace media {
26 
27 const int64_t kPositionNotSpecified = -1;
28 
29 class ResourceFetchContext;
30 class UrlData;
31 class UrlIndexTest;
32 
33 // A multibuffer for loading media resources which knows
34 // how to create MultiBufferDataProviders to load data
35 // into the cache.
36 class MEDIA_BLINK_EXPORT ResourceMultiBuffer : public MultiBuffer {
37  public:
38   ResourceMultiBuffer(UrlData* url_data_, int block_shift);
39   ~ResourceMultiBuffer() override;
40 
41   // MultiBuffer implementation.
42   std::unique_ptr<MultiBuffer::DataProvider> CreateWriter(
43       const BlockId& pos,
44       bool is_client_audio_element) override;
45   bool RangeSupported() const override;
46   void OnEmpty() override;
47 
48  protected:
49   // Do not access from destructor, it is a pointer to the
50   // object that contains us.
51   UrlData* url_data_;
52 };
53 
54 class UrlIndex;
55 
56 // All the data & metadata for a single resource.
57 // Data is cached using a MultiBuffer instance.
58 class MEDIA_BLINK_EXPORT UrlData : public base::RefCounted<UrlData> {
59  public:
60   // Keep in sync with WebMediaPlayer::CorsMode.
61   enum CorsMode { CORS_UNSPECIFIED, CORS_ANONYMOUS, CORS_USE_CREDENTIALS };
62   typedef std::pair<GURL, CorsMode> KeyType;
63 
64   // Accessors
url()65   const GURL& url() const { return url_; }
66 
67   // Cross-origin access mode
cors_mode()68   CorsMode cors_mode() const { return cors_mode_; }
69 
has_access_control()70   bool has_access_control() const { return has_access_control_; }
71 
72   // Are HTTP range requests supported?
range_supported()73   bool range_supported() const { return range_supported_; }
74 
75   // True if we found a reason why this URL won't be stored in the
76   // HTTP disk cache.
cacheable()77   bool cacheable() const { return cacheable_; }
78 
79   // Last used time.
last_used()80   base::Time last_used() const { return last_used_; }
81 
82   // Last modified time.
last_modified()83   base::Time last_modified() const { return last_modified_; }
84 
etag()85   const std::string& etag() const { return etag_; }
86 
87   // Expiration time.
valid_until()88   base::Time valid_until() const { return valid_until_; }
89 
90   // The key used by UrlIndex to find this UrlData.
91   KeyType key() const;
92 
93   // Length of data associated with url or |kPositionNotSpecified|
length()94   int64_t length() const { return length_; }
95 
96   // Returns the number of blocks cached for this resource.
97   size_t CachedSize();
98 
99   // Returns true if this resource is fully cached in memory.
100   bool FullyCached();
101 
102   // Returns our url_index.
url_index()103   UrlIndex* url_index() const { return url_index_; }
104 
105   // This must be called after the response arrives.
is_cors_cross_origin()106   bool is_cors_cross_origin() const { return is_cors_cross_origin_; }
107 
108   // Notifies the url index that this is currently used.
109   // The url <-> URLData mapping will be eventually be invalidated if
110   // this is not called regularly.
111   void Use();
112 
113   // Call this before we add some data to the multibuffer().
114   // If the multibuffer is empty, the data origin is set from
115   // |origin| and returns true. If not, it compares |origin|
116   // to the previous origin and returns whether they match or not.
117   bool ValidateDataOrigin(const GURL& origin);
118 
119   // Setters.
120   void set_length(int64_t length);
121   void set_cacheable(bool cacheable);
122   void set_valid_until(base::Time valid_until);
123   void set_range_supported();
124   void set_last_modified(base::Time last_modified);
125   void set_etag(const std::string& etag);
126   void set_is_cors_cross_origin(bool is_cors_cross_origin);
127   void set_has_access_control();
128 
129   // A redirect has occurred (or we've found a better UrlData for the same
130   // resource).
131   void RedirectTo(const scoped_refptr<UrlData>& to);
132 
133   // Fail, tell all clients that a failure has occurred.
134   void Fail();
135 
136   // Callback for receiving notifications when a redirect occurs.
137   using RedirectCB = base::OnceCallback<void(const scoped_refptr<UrlData>&)>;
138 
139   // Register a callback to be called when a redirect occurs.
140   // Callbacks are cleared when a redirect occurs, so clients must call
141   // OnRedirect again if they wish to continue receiving callbacks.
142   void OnRedirect(RedirectCB cb);
143 
144   // Returns true it is valid to keep using this to access cached data.
145   // A single media player instance may choose to ignore this for resources
146   // that have already been opened.
147   bool Valid();
148 
149   // Virtual so we can override it for testing.
150   virtual ResourceMultiBuffer* multibuffer();
151 
AddBytesRead(int64_t b)152   void AddBytesRead(int64_t b) { bytes_read_from_cache_ += b; }
BytesReadFromCache()153   int64_t BytesReadFromCache() const { return bytes_read_from_cache_; }
154 
155  protected:
156   UrlData(const GURL& url, CorsMode cors_mode, UrlIndex* url_index);
157   virtual ~UrlData();
158 
159  private:
160   friend class ResourceMultiBuffer;
161   friend class UrlIndex;
162   friend class UrlIndexTest;
163   friend class base::RefCounted<UrlData>;
164 
165   void OnEmpty();
166   void MergeFrom(const scoped_refptr<UrlData>& other);
167 
168   // Url we represent, note that there may be multiple UrlData for
169   // the same url.
170   const GURL url_;
171 
172   // Origin of the data, should only be different from the url_.GetOrigin()
173   // when service workers are involved.
174   GURL data_origin_;
175   bool have_data_origin_;
176 
177   // Cross-origin access mode.
178   const CorsMode cors_mode_;
179   bool has_access_control_;
180 
181   UrlIndex* const url_index_;
182 
183   // Length of resource this url points to. (in bytes)
184   int64_t length_;
185 
186   // Number of bytes read from this resource.
187   int64_t bytes_read_from_cache_ = 0;
188 
189   // Does the server support ranges?
190   bool range_supported_;
191 
192   // Set to false if we have reason to believe the chrome disk cache
193   // will not cache this url.
194   bool cacheable_;
195 
196   // https://html.spec.whatwg.org/#cors-cross-origin
197   bool is_cors_cross_origin_ = false;
198 
199   // Last time some media time used this resource.
200   // Note that we use base::Time rather than base::TimeTicks because
201   // TimeTicks will stop advancing when a machine goes to sleep.
202   // base::Time can go backwards, jump hours at a time and be generally
203   // unpredictable, but it doesn't stop, which is preferable here.
204   // (False negatives are better than false positivies.)
205   base::Time last_used_;
206 
207   // Expiration time according to http headers.
208   base::Time valid_until_;
209 
210   // Last modification time according to http headers.
211   base::Time last_modified_;
212 
213   // Etag from HTTP reply.
214   std::string etag_;
215 
216   ResourceMultiBuffer multibuffer_;
217   std::vector<RedirectCB> redirect_callbacks_;
218 
219   base::ThreadChecker thread_checker_;
220   DISALLOW_COPY_AND_ASSIGN(UrlData);
221 };
222 
223 // The UrlIndex lets you look up UrlData instances by url.
224 class MEDIA_BLINK_EXPORT UrlIndex {
225  public:
226   explicit UrlIndex(ResourceFetchContext* fetch_context);
227   UrlIndex(ResourceFetchContext* fetch_context, int block_shift);
228   virtual ~UrlIndex();
229 
230   // Look up an UrlData in the index and return it. If none is found,
231   // create a new one. Note that newly created UrlData entries are NOT
232   // added to the index, instead you must call TryInsert on them after
233   // initializing relevant parameters, like whether it support
234   // ranges and it's last modified time.
235   // Because the returned UrlData has a raw reference to |this|, it must be
236   // released before |this| is destroyed.
237   scoped_refptr<UrlData> GetByUrl(const GURL& gurl,
238                                   UrlData::CorsMode cors_mode);
239 
240   // Add the given UrlData to the index if possible. If a better UrlData
241   // is already present in the index, return it instead. (If not, we just
242   // return the given UrlData.) Please make sure to initialize all the data
243   // that can be gathered from HTTP headers in |url_data| before calling this.
244   // In particular, the following fields are important:
245   //   o range_supported: Entries which do not support ranges cannot be
246   //     shared and are not added to the index.
247   //   o valid_until, last_used: Entries have to be valid to be inserted
248   //     into the index, this means that they have to have been recently
249   //     used or have an Expires: header that says when they stop being valid.
250   //   o last_modified: Expired cache entries can be re-used if last_modified
251   //     matches.
252   // Because the returned UrlData has a raw reference to |this|, it must be
253   // released before |this| is destroyed.
254   // TODO(hubbe): Add etag support.
255   scoped_refptr<UrlData> TryInsert(const scoped_refptr<UrlData>& url_data);
256 
fetch_context()257   ResourceFetchContext* fetch_context() const { return fetch_context_; }
block_shift()258   int block_shift() const { return block_shift_; }
259 
260   // Returns true kMaxParallelPreload or more urls are loading at the same time.
261   bool HasReachedMaxParallelPreload() const;
262 
263   // Protected rather than private for testing.
264  protected:
265   friend class UrlData;
266   friend class ResourceMultiBuffer;
267   friend class UrlIndexTest;
268   void RemoveUrlData(const scoped_refptr<UrlData>& url_data);
269 
270   // Virtual so we can override it in tests.
271   virtual scoped_refptr<UrlData> NewUrlData(const GURL& url,
272                                             UrlData::CorsMode cors_mode);
273 
274   void OnMemoryPressure(
275       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
276 
277   ResourceFetchContext* fetch_context_;
278   using UrlDataMap = std::map<UrlData::KeyType, scoped_refptr<UrlData>>;
279   UrlDataMap indexed_data_;
280   scoped_refptr<MultiBuffer::GlobalLRU> lru_;
281 
282   // log2 of block size in multibuffer cache. Defaults to kBlockSizeShift.
283   // Currently only changed for testing purposes.
284   const int block_shift_;
285 
286   std::deque<scoped_refptr<UrlData>> loading_queue_;
287 
288   base::MemoryPressureListener memory_pressure_listener_;
289 };
290 
291 }  // namespace media
292 #endif  // MEDIA_BLINK_URL_INDEX_H_
293