1 // Copyright 2018 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 "services/network/conditional_cache_deletion_helper.h"
6 
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/threading/thread_task_runner_handle.h"
12 #include "net/http/http_cache.h"
13 #include "net/http/http_util.h"
14 
15 namespace {
16 
EntryPredicateFromURLsAndTime(const base::RepeatingCallback<bool (const GURL &)> & url_matcher,const base::Time & begin_time,const base::Time & end_time,const disk_cache::Entry * entry)17 bool EntryPredicateFromURLsAndTime(
18     const base::RepeatingCallback<bool(const GURL&)>& url_matcher,
19     const base::Time& begin_time,
20     const base::Time& end_time,
21     const disk_cache::Entry* entry) {
22   std::string entry_key(entry->GetKey());
23   std::string url_string(
24       net::HttpCache::GetResourceURLFromHttpCacheKey(entry_key));
25   return (entry->GetLastUsed() >= begin_time &&
26           entry->GetLastUsed() < end_time && url_matcher.Run(GURL(url_string)));
27 }
28 
29 }  // namespace
30 
31 namespace network {
32 
33 // static
34 std::unique_ptr<ConditionalCacheDeletionHelper>
CreateAndStart(disk_cache::Backend * cache,const base::RepeatingCallback<bool (const GURL &)> & url_matcher,const base::Time & begin_time,const base::Time & end_time,base::OnceClosure completion_callback)35 ConditionalCacheDeletionHelper::CreateAndStart(
36     disk_cache::Backend* cache,
37     const base::RepeatingCallback<bool(const GURL&)>& url_matcher,
38     const base::Time& begin_time,
39     const base::Time& end_time,
40     base::OnceClosure completion_callback) {
41   std::unique_ptr<ConditionalCacheDeletionHelper> deletion_helper(
42       new ConditionalCacheDeletionHelper(
43           base::BindRepeating(
44               &EntryPredicateFromURLsAndTime, url_matcher,
45               begin_time.is_null() ? base::Time() : begin_time,
46               end_time.is_null() ? base::Time::Max() : end_time),
47           std::move(completion_callback), cache->CreateIterator()));
48 
49   // Any status other than OK (since no entry), IO_PENDING, or FAILED would
50   // work here.
51   deletion_helper->IterateOverEntries(
52       disk_cache::EntryResult::MakeError(net::ERR_CACHE_OPEN_FAILURE));
53   return deletion_helper;
54 }
55 
ConditionalCacheDeletionHelper(const base::RepeatingCallback<bool (const disk_cache::Entry *)> & condition,base::OnceClosure completion_callback,std::unique_ptr<disk_cache::Backend::Iterator> iterator)56 ConditionalCacheDeletionHelper::ConditionalCacheDeletionHelper(
57     const base::RepeatingCallback<bool(const disk_cache::Entry*)>& condition,
58     base::OnceClosure completion_callback,
59     std::unique_ptr<disk_cache::Backend::Iterator> iterator)
60     : condition_(condition),
61       completion_callback_(std::move(completion_callback)),
62       iterator_(std::move(iterator)) {}
63 
64 ConditionalCacheDeletionHelper::~ConditionalCacheDeletionHelper() = default;
65 
IterateOverEntries(disk_cache::EntryResult result)66 void ConditionalCacheDeletionHelper::IterateOverEntries(
67     disk_cache::EntryResult result) {
68   while (result.net_error() != net::ERR_IO_PENDING) {
69     // If the entry obtained in the previous iteration matches the condition,
70     // mark it for deletion. The iterator is already one step forward, so it
71     // won't be invalidated. Always close the previous entry so it does not
72     // leak.
73     if (previous_entry_) {
74       if (condition_.Run(previous_entry_)) {
75         previous_entry_->Doom();
76       }
77       previous_entry_->Close();
78     }
79 
80     if (result.net_error() == net::ERR_FAILED) {
81       // The iteration finished successfully or we can no longer iterate
82       // (e.g. the cache was destroyed). We cannot distinguish between the two,
83       // but we know that there is nothing more that we can do.
84       base::ThreadTaskRunnerHandle::Get()->PostTask(
85           FROM_HERE,
86           base::BindOnce(&ConditionalCacheDeletionHelper::NotifyCompletion,
87                          weak_factory_.GetWeakPtr()));
88       return;
89     }
90 
91     previous_entry_ = result.ReleaseEntry();
92     result = iterator_->OpenNextEntry(
93         base::BindRepeating(&ConditionalCacheDeletionHelper::IterateOverEntries,
94                             weak_factory_.GetWeakPtr()));
95   }
96 }
97 
NotifyCompletion()98 void ConditionalCacheDeletionHelper::NotifyCompletion() {
99   std::move(completion_callback_).Run();
100 }
101 
102 }  // namespace network
103