1 // Copyright 2017 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 "content/browser/indexed_db/indexed_db_pre_close_task_queue.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/threading/sequenced_task_runner_handle.h"
12 #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
13 #include "third_party/leveldatabase/env_chromium.h"
14 
15 using blink::IndexedDBDatabaseMetadata;
16 
17 namespace content {
18 
PreCloseTask(leveldb::DB * database)19 IndexedDBPreCloseTaskQueue::PreCloseTask::PreCloseTask(leveldb::DB* database)
20     : database_(database) {}
21 
22 IndexedDBPreCloseTaskQueue::PreCloseTask::~PreCloseTask() = default;
23 
RequiresMetadata() const24 bool IndexedDBPreCloseTaskQueue::PreCloseTask::RequiresMetadata() const {
25   return false;
26 }
27 
SetMetadata(const std::vector<blink::IndexedDBDatabaseMetadata> * metadata)28 void IndexedDBPreCloseTaskQueue::PreCloseTask::SetMetadata(
29     const std::vector<blink::IndexedDBDatabaseMetadata>* metadata) {}
30 
IndexedDBPreCloseTaskQueue(std::list<std::unique_ptr<IndexedDBPreCloseTaskQueue::PreCloseTask>> tasks,base::OnceClosure on_complete,base::TimeDelta max_run_time,std::unique_ptr<base::OneShotTimer> timer)31 IndexedDBPreCloseTaskQueue::IndexedDBPreCloseTaskQueue(
32     std::list<std::unique_ptr<IndexedDBPreCloseTaskQueue::PreCloseTask>> tasks,
33     base::OnceClosure on_complete,
34     base::TimeDelta max_run_time,
35     std::unique_ptr<base::OneShotTimer> timer)
36     : tasks_(std::move(tasks)),
37       on_done_(std::move(on_complete)),
38       timeout_time_(max_run_time),
39       timeout_timer_(std::move(timer)),
40       task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
41 IndexedDBPreCloseTaskQueue::~IndexedDBPreCloseTaskQueue() = default;
42 
StopForNewConnection()43 void IndexedDBPreCloseTaskQueue::StopForNewConnection() {
44   if (!started_ || done_)
45     return;
46   DCHECK(!tasks_.empty());
47   while (!tasks_.empty()) {
48     tasks_.front()->Stop(StopReason::NEW_CONNECTION);
49     tasks_.pop_front();
50   }
51   OnComplete();
52 }
53 
Start(MetadataFetcher metadata_fetcher)54 void IndexedDBPreCloseTaskQueue::Start(MetadataFetcher metadata_fetcher) {
55   DCHECK(!started_);
56   started_ = true;
57   if (tasks_.empty()) {
58     OnComplete();
59     return;
60   }
61   timeout_timer_->Start(
62       FROM_HERE, timeout_time_,
63       base::BindOnce(&IndexedDBPreCloseTaskQueue::StopForTimout,
64                      ptr_factory_.GetWeakPtr()));
65   metadata_fetcher_ = std::move(metadata_fetcher);
66   task_runner_->PostTask(FROM_HERE,
67                          base::BindOnce(&IndexedDBPreCloseTaskQueue::RunLoop,
68                                         ptr_factory_.GetWeakPtr()));
69 }
70 
OnComplete()71 void IndexedDBPreCloseTaskQueue::OnComplete() {
72   DCHECK(started_);
73   DCHECK(!done_);
74   ptr_factory_.InvalidateWeakPtrs();
75   timeout_timer_->Stop();
76   done_ = true;
77   std::move(on_done_).Run();
78 }
79 
StopForTimout()80 void IndexedDBPreCloseTaskQueue::StopForTimout() {
81   DCHECK(started_);
82   if (done_)
83     return;
84   while (!tasks_.empty()) {
85     tasks_.front()->Stop(StopReason::TIMEOUT);
86     tasks_.pop_front();
87   }
88   OnComplete();
89 }
90 
StopForMetadataError(const leveldb::Status & status)91 void IndexedDBPreCloseTaskQueue::StopForMetadataError(
92     const leveldb::Status& status) {
93   if (done_)
94     return;
95 
96   LOCAL_HISTOGRAM_ENUMERATION(
97       "WebCore.IndexedDB.IndexedDBPreCloseTaskList.MetadataError",
98       leveldb_env::GetLevelDBStatusUMAValue(status),
99       leveldb_env::LEVELDB_STATUS_MAX);
100   while (!tasks_.empty()) {
101     tasks_.front()->Stop(StopReason::METADATA_ERROR);
102     tasks_.pop_front();
103   }
104   OnComplete();
105 }
106 
RunLoop()107 void IndexedDBPreCloseTaskQueue::RunLoop() {
108   if (done_)
109     return;
110 
111   if (tasks_.empty()) {
112     OnComplete();
113     return;
114   }
115 
116   PreCloseTask* task = tasks_.front().get();
117   if (task->RequiresMetadata() && !task->set_metadata_was_called_) {
118     if (!has_metadata_) {
119       leveldb::Status status = std::move(metadata_fetcher_).Run(&metadata_);
120       has_metadata_ = true;
121       if (!status.ok()) {
122         StopForMetadataError(status);
123         return;
124       }
125     }
126     task->SetMetadata(&metadata_);
127     task->set_metadata_was_called_ = true;
128   }
129   bool done = task->RunRound();
130   if (done) {
131     tasks_.pop_front();
132     if (tasks_.empty()) {
133       OnComplete();
134       return;
135     }
136   }
137   task_runner_->PostTask(FROM_HERE,
138                          base::BindOnce(&IndexedDBPreCloseTaskQueue::RunLoop,
139                                         ptr_factory_.GetWeakPtr()));
140 }
141 
142 }  // namespace content
143