1 // Copyright 2014 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 #include "third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10 #include "base/macros.h"
11 #include "base/memory/scoped_refptr.h"
12 #include "mojo/public/cpp/bindings/associated_remote.h"
13 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
14 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
15 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
16 #include "third_party/blink/public/common/cache_storage/cache_storage_utils.h"
17 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom-blink.h"
18 #include "third_party/blink/public/platform/platform.h"
19 #include "third_party/blink/public/platform/web_security_origin.h"
20 #include "third_party/blink/public/platform/web_string.h"
21 #include "third_party/blink/public/platform/web_url.h"
22 #include "third_party/blink/public/platform/web_vector.h"
23 #include "third_party/blink/renderer/core/dom/document.h"
24 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
25 #include "third_party/blink/renderer/core/fileapi/file_reader_loader.h"
26 #include "third_party/blink/renderer/core/fileapi/file_reader_loader_client.h"
27 #include "third_party/blink/renderer/core/frame/local_frame.h"
28 #include "third_party/blink/renderer/core/inspector/inspected_frames.h"
29 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
30 #include "third_party/blink/renderer/platform/blob/blob_data.h"
31 #include "third_party/blink/renderer/platform/heap/handle.h"
32 #include "third_party/blink/renderer/platform/network/http_header_map.h"
33 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
34 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
35 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
36 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
37 #include "third_party/blink/renderer/platform/wtf/functional.h"
38 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
39 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
40 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
41 #include "third_party/blink/renderer/platform/wtf/vector.h"
42 
43 using blink::protocol::Array;
44 // Renaming Cache since there is another blink::Cache.
45 using ProtocolCache = blink::protocol::CacheStorage::Cache;
46 using blink::protocol::CacheStorage::Cache;
47 using blink::protocol::CacheStorage::CachedResponse;
48 using blink::protocol::CacheStorage::CachedResponseType;
49 using blink::protocol::CacheStorage::DataEntry;
50 using blink::protocol::CacheStorage::Header;
51 // Renaming Response since there is another blink::Response.
52 using ProtocolResponse = blink::protocol::Response;
53 
54 using DeleteCacheCallback =
55     blink::protocol::CacheStorage::Backend::DeleteCacheCallback;
56 using DeleteEntryCallback =
57     blink::protocol::CacheStorage::Backend::DeleteEntryCallback;
58 using RequestCacheNamesCallback =
59     blink::protocol::CacheStorage::Backend::RequestCacheNamesCallback;
60 using RequestEntriesCallback =
61     blink::protocol::CacheStorage::Backend::RequestEntriesCallback;
62 using RequestCachedResponseCallback =
63     blink::protocol::CacheStorage::Backend::RequestCachedResponseCallback;
64 
65 namespace blink {
66 
67 namespace {
68 
BuildCacheId(const String & security_origin,const String & cache_name)69 String BuildCacheId(const String& security_origin, const String& cache_name) {
70   StringBuilder id;
71   id.Append(security_origin);
72   id.Append('|');
73   id.Append(cache_name);
74   return id.ToString();
75 }
76 
ParseCacheId(const String & id,String * security_origin,String * cache_name)77 ProtocolResponse ParseCacheId(const String& id,
78                               String* security_origin,
79                               String* cache_name) {
80   wtf_size_t pipe = id.find('|');
81   if (pipe == WTF::kNotFound)
82     return ProtocolResponse::ServerError("Invalid cache id.");
83   *security_origin = id.Substring(0, pipe);
84   *cache_name = id.Substring(pipe + 1);
85   return ProtocolResponse::Success();
86 }
87 
GetExecutionContext(InspectedFrames * frames,const String & security_origin,ExecutionContext ** context)88 ProtocolResponse GetExecutionContext(InspectedFrames* frames,
89                                      const String& security_origin,
90                                      ExecutionContext** context) {
91   LocalFrame* frame = frames->FrameWithSecurityOrigin(security_origin);
92   if (!frame) {
93     String msg = "No frame with origin " + security_origin;
94     return ProtocolResponse::ServerError(msg.Utf8());
95   }
96 
97   blink::Document* document = frame->GetDocument();
98   if (!document)
99     return ProtocolResponse::ServerError("No execution context found");
100 
101   *context = document->ToExecutionContext();
102 
103   return ProtocolResponse::Success();
104 }
105 
AssertCacheStorage(const String & security_origin,InspectedFrames * frames,InspectorCacheStorageAgent::CachesMap * caches,mojom::blink::CacheStorage ** result)106 ProtocolResponse AssertCacheStorage(
107     const String& security_origin,
108     InspectedFrames* frames,
109     InspectorCacheStorageAgent::CachesMap* caches,
110     mojom::blink::CacheStorage** result) {
111   scoped_refptr<const SecurityOrigin> sec_origin =
112       SecurityOrigin::CreateFromString(security_origin);
113 
114   // Cache Storage API is restricted to trustworthy origins.
115   if (!sec_origin->IsPotentiallyTrustworthy()) {
116     return ProtocolResponse::ServerError(
117         sec_origin->IsPotentiallyTrustworthyErrorMessage().Utf8());
118   }
119 
120   ExecutionContext* context = nullptr;
121   ProtocolResponse response =
122       GetExecutionContext(frames, security_origin, &context);
123   if (!response.IsSuccess())
124     return response;
125 
126   auto it = caches->find(security_origin);
127 
128   if (it == caches->end()) {
129     mojo::Remote<mojom::blink::CacheStorage> cache_storage_remote;
130     context->GetBrowserInterfaceBroker().GetInterface(
131         cache_storage_remote.BindNewPipeAndPassReceiver());
132     *result = cache_storage_remote.get();
133     caches->Set(security_origin, std::move(cache_storage_remote));
134   } else {
135     *result = it->value.get();
136   }
137 
138   return ProtocolResponse::Success();
139 }
140 
AssertCacheStorageAndNameForId(const String & cache_id,InspectedFrames * frames,String * cache_name,InspectorCacheStorageAgent::CachesMap * caches,mojom::blink::CacheStorage ** result)141 ProtocolResponse AssertCacheStorageAndNameForId(
142     const String& cache_id,
143     InspectedFrames* frames,
144     String* cache_name,
145     InspectorCacheStorageAgent::CachesMap* caches,
146     mojom::blink::CacheStorage** result) {
147   String security_origin;
148   ProtocolResponse response =
149       ParseCacheId(cache_id, &security_origin, cache_name);
150   if (!response.IsSuccess())
151     return response;
152   return AssertCacheStorage(security_origin, frames, caches, result);
153 }
154 
CacheStorageErrorString(mojom::blink::CacheStorageError error)155 const char* CacheStorageErrorString(mojom::blink::CacheStorageError error) {
156   switch (error) {
157     case mojom::blink::CacheStorageError::kErrorNotImplemented:
158       return "not implemented.";
159     case mojom::blink::CacheStorageError::kErrorNotFound:
160       return "not found.";
161     case mojom::blink::CacheStorageError::kErrorExists:
162       return "cache already exists.";
163     case mojom::blink::CacheStorageError::kErrorQuotaExceeded:
164       return "quota exceeded.";
165     case mojom::blink::CacheStorageError::kErrorCacheNameNotFound:
166       return "cache not found.";
167     case mojom::blink::CacheStorageError::kErrorQueryTooLarge:
168       return "operation too large.";
169     case mojom::blink::CacheStorageError::kErrorStorage:
170       return "storage failure.";
171     case mojom::blink::CacheStorageError::kErrorDuplicateOperation:
172       return "duplicate operation.";
173     case mojom::blink::CacheStorageError::kErrorCrossOriginResourcePolicy:
174       return "failed Cross-Origin-Resource-Policy check.";
175     case mojom::blink::CacheStorageError::kSuccess:
176       // This function should only be called upon error.
177       break;
178   }
179   NOTREACHED();
180   return "";
181 }
182 
ResponseTypeToString(network::mojom::FetchResponseType type)183 CachedResponseType ResponseTypeToString(
184     network::mojom::FetchResponseType type) {
185   switch (type) {
186     case network::mojom::FetchResponseType::kBasic:
187       return protocol::CacheStorage::CachedResponseTypeEnum::Basic;
188     case network::mojom::FetchResponseType::kCors:
189       return protocol::CacheStorage::CachedResponseTypeEnum::Cors;
190     case network::mojom::FetchResponseType::kDefault:
191       return protocol::CacheStorage::CachedResponseTypeEnum::Default;
192     case network::mojom::FetchResponseType::kError:
193       return protocol::CacheStorage::CachedResponseTypeEnum::Error;
194     case network::mojom::FetchResponseType::kOpaque:
195       return protocol::CacheStorage::CachedResponseTypeEnum::OpaqueResponse;
196     case network::mojom::FetchResponseType::kOpaqueRedirect:
197       return protocol::CacheStorage::CachedResponseTypeEnum::OpaqueRedirect;
198   }
199   NOTREACHED();
200   return "";
201 }
202 
203 struct DataRequestParams {
204   String cache_name;
205   int skip_count;
206   // If set to -1, pagination is disabled and all available requests are
207   // returned.
208   int page_size;
209   String path_filter;
210 };
211 
212 struct RequestResponse {
213   String request_url;
214   String request_method;
215   HTTPHeaderMap request_headers;
216   int response_status;
217   String response_status_text;
218   double response_time;
219   network::mojom::FetchResponseType response_type;
220   HTTPHeaderMap response_headers;
221 };
222 
223 class ResponsesAccumulator : public RefCounted<ResponsesAccumulator> {
224  public:
ResponsesAccumulator(wtf_size_t num_responses,const DataRequestParams & params,mojo::PendingAssociatedRemote<mojom::blink::CacheStorageCache> cache_pending_remote,std::unique_ptr<RequestEntriesCallback> callback)225   ResponsesAccumulator(
226       wtf_size_t num_responses,
227       const DataRequestParams& params,
228       mojo::PendingAssociatedRemote<mojom::blink::CacheStorageCache>
229           cache_pending_remote,
230       std::unique_ptr<RequestEntriesCallback> callback)
231       : params_(params),
232         num_responses_left_(num_responses),
233         cache_remote_(std::move(cache_pending_remote)),
234         callback_(std::move(callback)) {}
235 
Dispatch(Vector<mojom::blink::FetchAPIRequestPtr> old_requests)236   void Dispatch(Vector<mojom::blink::FetchAPIRequestPtr> old_requests) {
237     int64_t trace_id = blink::cache_storage::CreateTraceId();
238     TRACE_EVENT_WITH_FLOW0("CacheStorage", "ResponsesAccumulator::Dispatch",
239                            TRACE_ID_GLOBAL(trace_id),
240                            TRACE_EVENT_FLAG_FLOW_OUT);
241 
242     Vector<mojom::blink::FetchAPIRequestPtr> requests;
243     if (params_.path_filter.IsEmpty()) {
244       requests = std::move(old_requests);
245     } else {
246       for (auto& request : old_requests) {
247         String urlPath(request->url.GetPath());
248         if (!urlPath.Contains(params_.path_filter,
249                               WTF::kTextCaseUnicodeInsensitive))
250           continue;
251         requests.push_back(std::move(request));
252       }
253     }
254     wtf_size_t requestSize = requests.size();
255     if (!requestSize) {
256       callback_->sendSuccess(std::make_unique<Array<DataEntry>>(), 0);
257       return;
258     }
259 
260     responses_ = Vector<RequestResponse>(requestSize);
261     num_responses_left_ = requestSize;
262     for (auto& request : requests) {
263       // All FetchAPIRequests in cache_storage code are supposed to not contain
264       // a body.
265       DCHECK(!request->blob && !request->body);
266       auto request_clone_without_body = mojom::blink::FetchAPIRequest::New(
267           request->mode, request->is_main_resource_load,
268           request->request_context_type, request->destination,
269           request->frame_type, request->url, request->method, request->headers,
270           nullptr /* blob */, nullptr /* body */, request->referrer.Clone(),
271           request->credentials_mode, request->cache_mode,
272           request->redirect_mode, request->integrity, request->priority,
273           request->fetch_window_id, request->keepalive, request->is_reload,
274           request->is_history_navigation);
275       cache_remote_->Match(
276           std::move(request), mojom::blink::CacheQueryOptions::New(),
277           false /* in_related_fetch_event */, trace_id,
278           WTF::Bind(
279               [](scoped_refptr<ResponsesAccumulator> accumulator,
280                  mojom::blink::FetchAPIRequestPtr request,
281                  mojom::blink::MatchResultPtr result) {
282                 if (result->is_status()) {
283                   accumulator->SendFailure(result->get_status());
284                 } else {
285                   accumulator->AddRequestResponsePair(request,
286                                                       result->get_response());
287                 }
288               },
289               scoped_refptr<ResponsesAccumulator>(this),
290               std::move(request_clone_without_body)));
291     }
292   }
293 
AddRequestResponsePair(const mojom::blink::FetchAPIRequestPtr & request,const mojom::blink::FetchAPIResponsePtr & response)294   void AddRequestResponsePair(
295       const mojom::blink::FetchAPIRequestPtr& request,
296       const mojom::blink::FetchAPIResponsePtr& response) {
297     DCHECK_GT(num_responses_left_, 0);
298     RequestResponse& next_request_response =
299         responses_.at(responses_.size() - num_responses_left_);
300 
301     next_request_response.request_url = request->url.GetString();
302     next_request_response.request_method = request->method;
303     for (const auto& header : request->headers) {
304       next_request_response.request_headers.Set(AtomicString(header.key),
305                                                 AtomicString(header.value));
306     }
307 
308     next_request_response.response_status = response->status_code;
309     next_request_response.response_status_text = response->status_text;
310     next_request_response.response_time = response->response_time.ToDoubleT();
311     next_request_response.response_type = response->response_type;
312     for (const auto& header : response->headers) {
313       next_request_response.response_headers.Set(AtomicString(header.key),
314                                                  AtomicString(header.value));
315     }
316 
317     if (--num_responses_left_ != 0)
318       return;
319 
320     std::sort(responses_.begin(), responses_.end(),
321               [](const RequestResponse& a, const RequestResponse& b) {
322                 return WTF::CodeUnitCompareLessThan(a.request_url,
323                                                     b.request_url);
324               });
325     size_t returned_entries_count = responses_.size();
326     if (params_.skip_count > 0)
327       responses_.EraseAt(0, params_.skip_count);
328     if (params_.page_size != -1 &&
329         static_cast<size_t>(params_.page_size) < responses_.size()) {
330       responses_.EraseAt(params_.page_size,
331                          responses_.size() - params_.page_size);
332     }
333     auto array = std::make_unique<protocol::Array<DataEntry>>();
334     for (const auto& request_response : responses_) {
335       std::unique_ptr<DataEntry> entry =
336           DataEntry::create()
337               .setRequestURL(request_response.request_url)
338               .setRequestMethod(request_response.request_method)
339               .setRequestHeaders(
340                   SerializeHeaders(request_response.request_headers))
341               .setResponseStatus(request_response.response_status)
342               .setResponseStatusText(request_response.response_status_text)
343               .setResponseTime(request_response.response_time)
344               .setResponseType(
345                   ResponseTypeToString(request_response.response_type))
346               .setResponseHeaders(
347                   SerializeHeaders(request_response.response_headers))
348               .build();
349       array->emplace_back(std::move(entry));
350     }
351 
352     callback_->sendSuccess(std::move(array), returned_entries_count);
353   }
354 
SendFailure(const mojom::blink::CacheStorageError & error)355   void SendFailure(const mojom::blink::CacheStorageError& error) {
356     callback_->sendFailure(ProtocolResponse::ServerError(
357         String::Format("Error requesting responses for cache %s : %s",
358                        params_.cache_name.Latin1().c_str(),
359                        CacheStorageErrorString(error))
360             .Utf8()));
361   }
362 
SerializeHeaders(const HTTPHeaderMap & headers)363   std::unique_ptr<Array<Header>> SerializeHeaders(
364       const HTTPHeaderMap& headers) {
365     auto result = std::make_unique<protocol::Array<Header>>();
366     for (HTTPHeaderMap::const_iterator it = headers.begin(),
367                                        end = headers.end();
368          it != end; ++it) {
369       result->emplace_back(
370           Header::create().setName(it->key).setValue(it->value).build());
371     }
372     return result;
373   }
374 
375  private:
376   DataRequestParams params_;
377   int num_responses_left_;
378   Vector<RequestResponse> responses_;
379   mojo::AssociatedRemote<mojom::blink::CacheStorageCache> cache_remote_;
380   std::unique_ptr<RequestEntriesCallback> callback_;
381 
382   DISALLOW_COPY_AND_ASSIGN(ResponsesAccumulator);
383 };
384 
385 class GetCacheKeysForRequestData {
386   USING_FAST_MALLOC(GetCacheKeysForRequestData);
387 
388  public:
GetCacheKeysForRequestData(const DataRequestParams & params,mojo::PendingAssociatedRemote<mojom::blink::CacheStorageCache> cache_pending_remote,std::unique_ptr<RequestEntriesCallback> callback)389   GetCacheKeysForRequestData(
390       const DataRequestParams& params,
391       mojo::PendingAssociatedRemote<mojom::blink::CacheStorageCache>
392           cache_pending_remote,
393       std::unique_ptr<RequestEntriesCallback> callback)
394       : params_(params), callback_(std::move(callback)) {
395     cache_remote_.Bind(std::move(cache_pending_remote));
396   }
397 
Dispatch(std::unique_ptr<GetCacheKeysForRequestData> self)398   void Dispatch(std::unique_ptr<GetCacheKeysForRequestData> self) {
399     int64_t trace_id = blink::cache_storage::CreateTraceId();
400     TRACE_EVENT_WITH_FLOW0(
401         "CacheStorage", "GetCacheKeysForRequestData::Dispatch",
402         TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
403     cache_remote_->Keys(
404         nullptr /* request */, mojom::blink::CacheQueryOptions::New(), trace_id,
405         WTF::Bind(
406             [](DataRequestParams params,
407                std::unique_ptr<GetCacheKeysForRequestData> self,
408                mojom::blink::CacheKeysResultPtr result) {
409               if (result->is_status()) {
410                 self->callback_->sendFailure(ProtocolResponse::ServerError(
411                     String::Format(
412                         "Error requesting requests for cache %s: %s",
413                         params.cache_name.Latin1().c_str(),
414                         CacheStorageErrorString(result->get_status()))
415                         .Utf8()));
416               } else {
417                 if (result->get_keys().IsEmpty()) {
418                   auto array = std::make_unique<protocol::Array<DataEntry>>();
419                   self->callback_->sendSuccess(std::move(array), 0);
420                   return;
421                 }
422                 scoped_refptr<ResponsesAccumulator> accumulator =
423                     base::AdoptRef(new ResponsesAccumulator(
424                         result->get_keys().size(), params,
425                         self->cache_remote_.Unbind(),
426                         std::move(self->callback_)));
427                 accumulator->Dispatch(std::move(result->get_keys()));
428               }
429             },
430             params_, std::move(self)));
431   }
432 
433  private:
434   DataRequestParams params_;
435   mojo::AssociatedRemote<mojom::blink::CacheStorageCache> cache_remote_;
436   std::unique_ptr<RequestEntriesCallback> callback_;
437 
438   DISALLOW_COPY_AND_ASSIGN(GetCacheKeysForRequestData);
439 };
440 
441 class CachedResponseFileReaderLoaderClient final
442     : private FileReaderLoaderClient {
443  public:
Load(scoped_refptr<BlobDataHandle> blob,std::unique_ptr<RequestCachedResponseCallback> callback)444   static void Load(scoped_refptr<BlobDataHandle> blob,
445                    std::unique_ptr<RequestCachedResponseCallback> callback) {
446     new CachedResponseFileReaderLoaderClient(std::move(blob),
447                                              std::move(callback));
448   }
449 
DidStartLoading()450   void DidStartLoading() override {}
451 
DidFinishLoading()452   void DidFinishLoading() override {
453     std::unique_ptr<CachedResponse> response =
454         CachedResponse::create()
455             .setBody(protocol::Binary::fromSharedBuffer(data_))
456             .build();
457     callback_->sendSuccess(std::move(response));
458     dispose();
459   }
460 
DidFail(FileErrorCode error)461   void DidFail(FileErrorCode error) override {
462     callback_->sendFailure(ProtocolResponse::ServerError(
463         String::Format("Unable to read the cached response, error code: %d",
464                        error)
465             .Utf8()));
466     dispose();
467   }
468 
DidReceiveDataForClient(const char * data,unsigned data_length)469   void DidReceiveDataForClient(const char* data,
470                                unsigned data_length) override {
471     data_->Append(data, data_length);
472   }
473 
474  private:
CachedResponseFileReaderLoaderClient(scoped_refptr<BlobDataHandle> && blob,std::unique_ptr<RequestCachedResponseCallback> && callback)475   CachedResponseFileReaderLoaderClient(
476       scoped_refptr<BlobDataHandle>&& blob,
477       std::unique_ptr<RequestCachedResponseCallback>&& callback)
478       // TODO(hajimehoshi): Use a per-ExecutionContext task runner of
479       // TaskType::kFileReading
480       : loader_(std::make_unique<FileReaderLoader>(
481             FileReaderLoader::kReadByClient,
482             static_cast<FileReaderLoaderClient*>(this),
483             ThreadScheduler::Current()->DeprecatedDefaultTaskRunner())),
484         callback_(std::move(callback)),
485         data_(SharedBuffer::Create()) {
486     loader_->Start(std::move(blob));
487   }
488 
489   ~CachedResponseFileReaderLoaderClient() override = default;
490 
dispose()491   void dispose() { delete this; }
492 
493   std::unique_ptr<FileReaderLoader> loader_;
494   std::unique_ptr<RequestCachedResponseCallback> callback_;
495   scoped_refptr<SharedBuffer> data_;
496 
497   DISALLOW_COPY_AND_ASSIGN(CachedResponseFileReaderLoaderClient);
498 };
499 
500 }  // namespace
501 
InspectorCacheStorageAgent(InspectedFrames * frames)502 InspectorCacheStorageAgent::InspectorCacheStorageAgent(InspectedFrames* frames)
503     : frames_(frames) {}
504 
505 InspectorCacheStorageAgent::~InspectorCacheStorageAgent() = default;
506 
Trace(Visitor * visitor)507 void InspectorCacheStorageAgent::Trace(Visitor* visitor) {
508   visitor->Trace(frames_);
509   InspectorBaseAgent::Trace(visitor);
510 }
511 
requestCacheNames(const String & security_origin,std::unique_ptr<RequestCacheNamesCallback> callback)512 void InspectorCacheStorageAgent::requestCacheNames(
513     const String& security_origin,
514     std::unique_ptr<RequestCacheNamesCallback> callback) {
515   int64_t trace_id = blink::cache_storage::CreateTraceId();
516   TRACE_EVENT_WITH_FLOW0("CacheStorage",
517                          "InspectorCacheStorageAgent::requestCacheNames",
518                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
519 
520   scoped_refptr<const SecurityOrigin> sec_origin =
521       SecurityOrigin::CreateFromString(security_origin);
522 
523   // Cache Storage API is restricted to trustworthy origins.
524   if (!sec_origin->IsPotentiallyTrustworthy()) {
525     // Don't treat this as an error, just don't attempt to open and enumerate
526     // the caches.
527     callback->sendSuccess(std::make_unique<protocol::Array<ProtocolCache>>());
528     return;
529   }
530 
531   mojom::blink::CacheStorage* cache_storage = nullptr;
532 
533   ProtocolResponse response =
534       AssertCacheStorage(security_origin, frames_, &caches_, &cache_storage);
535   if (!response.IsSuccess()) {
536     callback->sendFailure(response);
537     return;
538   }
539 
540   cache_storage->Keys(
541       trace_id,
542       WTF::Bind(
543           [](String security_origin,
544              std::unique_ptr<RequestCacheNamesCallback> callback,
545              const Vector<String>& caches) {
546             auto array = std::make_unique<protocol::Array<ProtocolCache>>();
547             for (auto& cache : caches) {
548               array->emplace_back(
549                   ProtocolCache::create()
550                       .setSecurityOrigin(security_origin)
551                       .setCacheName(cache)
552                       .setCacheId(BuildCacheId(security_origin, cache))
553                       .build());
554             }
555             callback->sendSuccess(std::move(array));
556           },
557           security_origin, std::move(callback)));
558 }
559 
requestEntries(const String & cache_id,protocol::Maybe<int> skip_count,protocol::Maybe<int> page_size,protocol::Maybe<String> path_filter,std::unique_ptr<RequestEntriesCallback> callback)560 void InspectorCacheStorageAgent::requestEntries(
561     const String& cache_id,
562     protocol::Maybe<int> skip_count,
563     protocol::Maybe<int> page_size,
564     protocol::Maybe<String> path_filter,
565     std::unique_ptr<RequestEntriesCallback> callback) {
566   int64_t trace_id = blink::cache_storage::CreateTraceId();
567   TRACE_EVENT_WITH_FLOW0("CacheStorage",
568                          "InspectorCacheStorageAgent::requestEntries",
569                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
570 
571   String cache_name;
572   mojom::blink::CacheStorage* cache_storage = nullptr;
573   ProtocolResponse response = AssertCacheStorageAndNameForId(
574       cache_id, frames_, &cache_name, &caches_, &cache_storage);
575   if (!response.IsSuccess()) {
576     callback->sendFailure(response);
577     return;
578   }
579   DataRequestParams params;
580   params.cache_name = cache_name;
581   params.page_size = page_size.fromMaybe(-1);
582   params.skip_count = skip_count.fromMaybe(0);
583   params.path_filter = path_filter.fromMaybe("");
584 
585   cache_storage->Open(
586       cache_name, trace_id,
587       WTF::Bind(
588           [](DataRequestParams params,
589              std::unique_ptr<RequestEntriesCallback> callback,
590              mojom::blink::OpenResultPtr result) {
591             if (result->is_status()) {
592               callback->sendFailure(ProtocolResponse::ServerError(
593                   String::Format("Error requesting cache %s: %s",
594                                  params.cache_name.Latin1().c_str(),
595                                  CacheStorageErrorString(result->get_status()))
596                       .Utf8()));
597             } else {
598               auto request = std::make_unique<GetCacheKeysForRequestData>(
599                   params, std::move(result->get_cache()), std::move(callback));
600               auto* request_ptr = request.get();
601               request_ptr->Dispatch(std::move(request));
602             }
603           },
604           params, std::move(callback)));
605 }
606 
deleteCache(const String & cache_id,std::unique_ptr<DeleteCacheCallback> callback)607 void InspectorCacheStorageAgent::deleteCache(
608     const String& cache_id,
609     std::unique_ptr<DeleteCacheCallback> callback) {
610   int64_t trace_id = blink::cache_storage::CreateTraceId();
611   TRACE_EVENT_WITH_FLOW0("CacheStorage",
612                          "InspectorCacheStorageAgent::deleteCache",
613                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
614 
615   String cache_name;
616   mojom::blink::CacheStorage* cache_storage = nullptr;
617   ProtocolResponse response = AssertCacheStorageAndNameForId(
618       cache_id, frames_, &cache_name, &caches_, &cache_storage);
619   if (!response.IsSuccess()) {
620     callback->sendFailure(response);
621     return;
622   }
623   cache_storage->Delete(
624       cache_name, trace_id,
625       WTF::Bind(
626           [](std::unique_ptr<DeleteCacheCallback> callback,
627              mojom::blink::CacheStorageError error) {
628             if (error == mojom::blink::CacheStorageError::kSuccess) {
629               callback->sendSuccess();
630             } else {
631               callback->sendFailure(ProtocolResponse::ServerError(
632                   String::Format("Error requesting cache names: %s",
633                                  CacheStorageErrorString(error))
634                       .Utf8()));
635             }
636           },
637           std::move(callback)));
638 }
639 
deleteEntry(const String & cache_id,const String & request,std::unique_ptr<DeleteEntryCallback> callback)640 void InspectorCacheStorageAgent::deleteEntry(
641     const String& cache_id,
642     const String& request,
643     std::unique_ptr<DeleteEntryCallback> callback) {
644   int64_t trace_id = blink::cache_storage::CreateTraceId();
645   TRACE_EVENT_WITH_FLOW0("CacheStorage",
646                          "InspectorCacheStorageAgent::deleteEntry",
647                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
648 
649   String cache_name;
650   mojom::blink::CacheStorage* cache_storage = nullptr;
651   ProtocolResponse response = AssertCacheStorageAndNameForId(
652       cache_id, frames_, &cache_name, &caches_, &cache_storage);
653   if (!response.IsSuccess()) {
654     callback->sendFailure(response);
655     return;
656   }
657   cache_storage->Open(
658       cache_name, trace_id,
659       WTF::Bind(
660           [](String cache_name, String request, int64_t trace_id,
661              std::unique_ptr<DeleteEntryCallback> callback,
662              mojom::blink::OpenResultPtr result) {
663             if (result->is_status()) {
664               callback->sendFailure(ProtocolResponse::ServerError(
665                   String::Format("Error requesting cache %s: %s",
666                                  cache_name.Latin1().c_str(),
667                                  CacheStorageErrorString(result->get_status()))
668                       .Utf8()));
669             } else {
670               Vector<mojom::blink::BatchOperationPtr> batch_operations;
671               batch_operations.push_back(mojom::blink::BatchOperation::New());
672               auto& operation = batch_operations.back();
673               operation->operation_type = mojom::blink::OperationType::kDelete;
674               operation->request = mojom::blink::FetchAPIRequest::New();
675               operation->request->url = KURL(request);
676               operation->request->method = String("GET");
677 
678               mojo::AssociatedRemote<mojom::blink::CacheStorageCache>
679                   cache_remote;
680               cache_remote.Bind(std::move(result->get_cache()));
681               auto* cache = cache_remote.get();
682               cache->Batch(
683                   std::move(batch_operations), trace_id,
684                   WTF::Bind(
685                       [](mojo::AssociatedRemote<mojom::blink::CacheStorageCache>
686                              cache_remote,
687                          std::unique_ptr<DeleteEntryCallback> callback,
688                          mojom::blink::CacheStorageVerboseErrorPtr error) {
689                         if (error->value !=
690                             mojom::blink::CacheStorageError::kSuccess) {
691                           callback->sendFailure(ProtocolResponse::ServerError(
692                               String::Format(
693                                   "Error deleting cache entry: %s",
694                                   CacheStorageErrorString(error->value))
695                                   .Utf8()));
696                         } else {
697                           callback->sendSuccess();
698                         }
699                       },
700                       std::move(cache_remote), std::move(callback)));
701             }
702           },
703           cache_name, request, trace_id, std::move(callback)));
704 }
705 
requestCachedResponse(const String & cache_id,const String & request_url,const std::unique_ptr<protocol::Array<protocol::CacheStorage::Header>> request_headers,std::unique_ptr<RequestCachedResponseCallback> callback)706 void InspectorCacheStorageAgent::requestCachedResponse(
707     const String& cache_id,
708     const String& request_url,
709     const std::unique_ptr<protocol::Array<protocol::CacheStorage::Header>>
710         request_headers,
711     std::unique_ptr<RequestCachedResponseCallback> callback) {
712   int64_t trace_id = blink::cache_storage::CreateTraceId();
713   TRACE_EVENT_WITH_FLOW0("CacheStorage",
714                          "InspectorCacheStorageAgent::requestCachedResponse",
715                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
716 
717   String cache_name;
718   mojom::blink::CacheStorage* cache_storage = nullptr;
719   ProtocolResponse response = AssertCacheStorageAndNameForId(
720       cache_id, frames_, &cache_name, &caches_, &cache_storage);
721   if (!response.IsSuccess()) {
722     callback->sendFailure(response);
723     return;
724   }
725   auto request = mojom::blink::FetchAPIRequest::New();
726   request->url = KURL(request_url);
727   request->method = String("GET");
728   for (const std::unique_ptr<protocol::CacheStorage::Header>& header :
729        *request_headers) {
730     request->headers.insert(header->getName(), header->getValue());
731   }
732 
733   auto multi_query_options = mojom::blink::MultiCacheQueryOptions::New();
734   multi_query_options->query_options = mojom::blink::CacheQueryOptions::New();
735   multi_query_options->cache_name = cache_name;
736 
737   cache_storage->Match(
738       std::move(request), std::move(multi_query_options),
739       false /* in_related_fetch_event */, trace_id,
740       WTF::Bind(
741           [](std::unique_ptr<RequestCachedResponseCallback> callback,
742              mojom::blink::MatchResultPtr result) {
743             if (result->is_status()) {
744               callback->sendFailure(ProtocolResponse::ServerError(
745                   String::Format("Unable to read cached response: %s",
746                                  CacheStorageErrorString(result->get_status()))
747                       .Utf8()));
748             } else {
749               std::unique_ptr<protocol::DictionaryValue> headers =
750                   protocol::DictionaryValue::create();
751               if (!result->get_response()->blob) {
752                 callback->sendSuccess(CachedResponse::create()
753                                           .setBody(protocol::Binary())
754                                           .build());
755                 return;
756               }
757               CachedResponseFileReaderLoaderClient::Load(
758                   std::move(result->get_response()->blob), std::move(callback));
759             }
760           },
761           std::move(callback)));
762 }
763 }  // namespace blink
764