Extract code handling PrinterProviderAPI from PrintPreviewHandler
[chromium-blink-merge.git] / chrome / browser / extensions / activity_log / activity_database.cc
blobd8c65e4c77b74317837da195b54b132bb123f950
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/activity_log/activity_database.h"
7 #include <string>
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/threading/thread.h"
14 #include "base/threading/thread_checker.h"
15 #include "base/time/clock.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "sql/error_delegate_util.h"
20 #include "sql/transaction.h"
21 #include "third_party/sqlite/sqlite3.h"
23 #if defined(OS_MACOSX)
24 #include "base/mac/mac_util.h"
25 #endif
27 using content::BrowserThread;
29 namespace extensions {
31 // A size threshold at which data should be flushed to the database. The
32 // ActivityDatabase will signal the Delegate to write out data based on a
33 // periodic timer, but will also initiate a flush if AdviseFlush indicates that
34 // more than kSizeThresholdForFlush action records are queued in memory. This
35 // should be set large enough that write costs can be amortized across many
36 // records, but not so large that too much space can be tied up holding records
37 // in memory.
38 static const int kSizeThresholdForFlush = 200;
40 ActivityDatabase::ActivityDatabase(ActivityDatabase::Delegate* delegate)
41 : delegate_(delegate),
42 valid_db_(false),
43 batch_mode_(true),
44 already_closed_(false),
45 did_init_(false) {
46 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
47 switches::kEnableExtensionActivityLogTesting)) {
48 batching_period_ = base::TimeDelta::FromSeconds(10);
49 } else {
50 batching_period_ = base::TimeDelta::FromMinutes(2);
54 ActivityDatabase::~ActivityDatabase() {}
56 void ActivityDatabase::Init(const base::FilePath& db_name) {
57 if (did_init_) return;
58 did_init_ = true;
59 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB))
60 DCHECK_CURRENTLY_ON(BrowserThread::DB);
61 db_.set_histogram_tag("Activity");
62 db_.set_error_callback(
63 base::Bind(&ActivityDatabase::DatabaseErrorCallback,
64 base::Unretained(this)));
65 db_.set_page_size(4096);
66 db_.set_cache_size(32);
68 if (!db_.Open(db_name)) {
69 LOG(ERROR) << db_.GetErrorMessage();
70 return LogInitFailure();
73 // Wrap the initialization in a transaction so that the db doesn't
74 // get corrupted if init fails/crashes.
75 sql::Transaction committer(&db_);
76 if (!committer.Begin())
77 return LogInitFailure();
79 #if defined(OS_MACOSX)
80 // Exclude the database from backups.
81 base::mac::SetFileBackupExclusion(db_name);
82 #endif
84 if (!delegate_->InitDatabase(&db_))
85 return LogInitFailure();
87 sql::InitStatus stat = committer.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
88 if (stat != sql::INIT_OK)
89 return LogInitFailure();
91 // Pre-loads the first <cache-size> pages into the cache.
92 // Doesn't do anything if the database is new.
93 db_.Preload();
95 valid_db_ = true;
96 timer_.Start(FROM_HERE,
97 batching_period_,
98 this,
99 &ActivityDatabase::RecordBatchedActions);
102 void ActivityDatabase::LogInitFailure() {
103 LOG(ERROR) << "Couldn't initialize the activity log database.";
104 SoftFailureClose();
107 void ActivityDatabase::AdviseFlush(int size) {
108 if (!valid_db_)
109 return;
110 if (!batch_mode_ || size == kFlushImmediately ||
111 size >= kSizeThresholdForFlush) {
112 if (!delegate_->FlushDatabase(&db_))
113 SoftFailureClose();
117 void ActivityDatabase::RecordBatchedActions() {
118 if (valid_db_) {
119 if (!delegate_->FlushDatabase(&db_))
120 SoftFailureClose();
124 void ActivityDatabase::SetBatchModeForTesting(bool batch_mode) {
125 if (batch_mode && !batch_mode_) {
126 timer_.Start(FROM_HERE,
127 batching_period_,
128 this,
129 &ActivityDatabase::RecordBatchedActions);
130 } else if (!batch_mode && batch_mode_) {
131 timer_.Stop();
132 RecordBatchedActions();
134 batch_mode_ = batch_mode;
137 sql::Connection* ActivityDatabase::GetSqlConnection() {
138 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB))
139 DCHECK_CURRENTLY_ON(BrowserThread::DB);
140 if (valid_db_) {
141 return &db_;
142 } else {
143 LOG(WARNING) << "Activity log database is not valid";
144 return NULL;
148 void ActivityDatabase::Close() {
149 timer_.Stop();
150 if (!already_closed_) {
151 RecordBatchedActions();
152 db_.reset_error_callback();
154 valid_db_ = false;
155 already_closed_ = true;
156 // Call DatabaseCloseCallback() just before deleting the ActivityDatabase
157 // itself--these two objects should have the same lifetime.
158 delegate_->OnDatabaseClose();
159 delete this;
162 void ActivityDatabase::HardFailureClose() {
163 if (already_closed_) return;
164 valid_db_ = false;
165 timer_.Stop();
166 db_.reset_error_callback();
167 db_.RazeAndClose();
168 delegate_->OnDatabaseFailure();
169 already_closed_ = true;
172 void ActivityDatabase::SoftFailureClose() {
173 valid_db_ = false;
174 timer_.Stop();
175 delegate_->OnDatabaseFailure();
178 void ActivityDatabase::DatabaseErrorCallback(int error, sql::Statement* stmt) {
179 if (sql::IsErrorCatastrophic(error)) {
180 LOG(ERROR) << "Killing the ActivityDatabase due to catastrophic error.";
181 HardFailureClose();
182 } else if (error != SQLITE_BUSY) {
183 // We ignore SQLITE_BUSY errors because they are presumably transient.
184 LOG(ERROR) << "Closing the ActivityDatabase due to error.";
185 SoftFailureClose();
189 void ActivityDatabase::RecordBatchedActionsWhileTesting() {
190 RecordBatchedActions();
191 timer_.Stop();
194 void ActivityDatabase::SetTimerForTesting(int ms) {
195 timer_.Stop();
196 timer_.Start(FROM_HERE,
197 base::TimeDelta::FromMilliseconds(ms),
198 this,
199 &ActivityDatabase::RecordBatchedActionsWhileTesting);
202 // static
203 bool ActivityDatabase::InitializeTable(sql::Connection* db,
204 const char* table_name,
205 const char* const content_fields[],
206 const char* const field_types[],
207 const int num_content_fields) {
208 if (!db->DoesTableExist(table_name)) {
209 std::string table_creator =
210 base::StringPrintf("CREATE TABLE %s (", table_name);
211 for (int i = 0; i < num_content_fields; i++) {
212 table_creator += base::StringPrintf("%s%s %s",
213 i == 0 ? "" : ", ",
214 content_fields[i],
215 field_types[i]);
217 table_creator += ")";
218 if (!db->Execute(table_creator.c_str()))
219 return false;
220 } else {
221 // In case we ever want to add new fields, this initializes them to be
222 // empty strings.
223 for (int i = 0; i < num_content_fields; i++) {
224 if (!db->DoesColumnExist(table_name, content_fields[i])) {
225 std::string table_updater = base::StringPrintf(
226 "ALTER TABLE %s ADD COLUMN %s %s; ",
227 table_name,
228 content_fields[i],
229 field_types[i]);
230 if (!db->Execute(table_updater.c_str()))
231 return false;
235 return true;
238 } // namespace extensions