Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / activity_log / fullstream_ui_policy.cc
blob9996fa59ec8be53c3dea8d6758a13608b388d008
1 // Copyright 2013 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/fullstream_ui_policy.h"
7 #include "base/callback.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_string_value_serializer.h"
12 #include "base/logging.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/stringprintf.h"
15 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
16 #include "chrome/browser/extensions/activity_log/activity_database.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/chrome_constants.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "extensions/common/dom_action_types.h"
21 #include "extensions/common/extension.h"
22 #include "sql/statement.h"
23 #include "sql/transaction.h"
24 #include "url/gurl.h"
26 using base::Callback;
27 using base::FilePath;
28 using base::Time;
29 using base::Unretained;
30 using content::BrowserThread;
32 namespace constants = activity_log_constants;
34 namespace extensions {
36 const char FullStreamUIPolicy::kTableName[] = "activitylog_full";
37 const char* const FullStreamUIPolicy::kTableContentFields[] = {
38 "extension_id", "time", "action_type", "api_name", "args", "page_url",
39 "page_title", "arg_url", "other"
41 const char* const FullStreamUIPolicy::kTableFieldTypes[] = {
42 "LONGVARCHAR NOT NULL", "INTEGER", "INTEGER", "LONGVARCHAR", "LONGVARCHAR",
43 "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR"
45 const int FullStreamUIPolicy::kTableFieldCount =
46 arraysize(FullStreamUIPolicy::kTableContentFields);
48 FullStreamUIPolicy::FullStreamUIPolicy(Profile* profile)
49 : ActivityLogDatabasePolicy(
50 profile,
51 FilePath(chrome::kExtensionActivityLogFilename)) {}
53 FullStreamUIPolicy::~FullStreamUIPolicy() {}
55 bool FullStreamUIPolicy::InitDatabase(sql::Connection* db) {
56 if (!Util::DropObsoleteTables(db))
57 return false;
59 // Create the unified activity log entry table.
60 return ActivityDatabase::InitializeTable(db,
61 kTableName,
62 kTableContentFields,
63 kTableFieldTypes,
64 arraysize(kTableContentFields));
67 bool FullStreamUIPolicy::FlushDatabase(sql::Connection* db) {
68 if (queued_actions_.empty())
69 return true;
71 sql::Transaction transaction(db);
72 if (!transaction.Begin())
73 return false;
75 std::string sql_str =
76 "INSERT INTO " + std::string(FullStreamUIPolicy::kTableName) +
77 " (extension_id, time, action_type, api_name, args, "
78 "page_url, page_title, arg_url, other) VALUES (?,?,?,?,?,?,?,?,?)";
79 sql::Statement statement(db->GetCachedStatement(
80 sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
82 Action::ActionVector::size_type i;
83 for (i = 0; i != queued_actions_.size(); ++i) {
84 const Action& action = *queued_actions_[i].get();
85 statement.Reset(true);
86 statement.BindString(0, action.extension_id());
87 statement.BindInt64(1, action.time().ToInternalValue());
88 statement.BindInt(2, static_cast<int>(action.action_type()));
89 statement.BindString(3, action.api_name());
90 if (action.args()) {
91 statement.BindString(4, Util::Serialize(action.args()));
93 std::string page_url_string = action.SerializePageUrl();
94 if (!page_url_string.empty()) {
95 statement.BindString(5, page_url_string);
97 if (!action.page_title().empty()) {
98 statement.BindString(6, action.page_title());
100 std::string arg_url_string = action.SerializeArgUrl();
101 if (!arg_url_string.empty()) {
102 statement.BindString(7, arg_url_string);
104 if (action.other()) {
105 statement.BindString(8, Util::Serialize(action.other()));
108 if (!statement.Run()) {
109 LOG(ERROR) << "Activity log database I/O failed: " << sql_str;
110 return false;
114 if (!transaction.Commit())
115 return false;
117 queued_actions_.clear();
118 return true;
121 scoped_ptr<Action::ActionVector> FullStreamUIPolicy::DoReadFilteredData(
122 const std::string& extension_id,
123 const Action::ActionType type,
124 const std::string& api_name,
125 const std::string& page_url,
126 const std::string& arg_url,
127 const int days_ago) {
128 // Ensure data is flushed to the database first so that we query over all
129 // data.
130 activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
131 scoped_ptr<Action::ActionVector> actions(new Action::ActionVector());
133 sql::Connection* db = GetDatabaseConnection();
134 if (!db) {
135 return actions.Pass();
138 // Build up the query based on which parameters were specified.
139 std::string where_str = "";
140 std::string where_next = "";
141 if (!extension_id.empty()) {
142 where_str += "extension_id=?";
143 where_next = " AND ";
145 if (!api_name.empty()) {
146 where_str += where_next + "api_name=?";
147 where_next = " AND ";
149 if (type != Action::ACTION_ANY) {
150 where_str += where_next + "action_type=?";
151 where_next = " AND ";
153 if (!page_url.empty()) {
154 where_str += where_next + "page_url LIKE ?";
155 where_next = " AND ";
157 if (!arg_url.empty()) {
158 where_str += where_next + "arg_url LIKE ?";
160 if (days_ago >= 0)
161 where_str += where_next + "time BETWEEN ? AND ?";
162 std::string query_str = base::StringPrintf(
163 "SELECT extension_id,time,action_type,api_name,args,page_url,page_title,"
164 "arg_url,other,rowid FROM %s %s %s ORDER BY time DESC LIMIT 300",
165 kTableName,
166 where_str.empty() ? "" : "WHERE",
167 where_str.c_str());
168 sql::Statement query(db->GetUniqueStatement(query_str.c_str()));
169 int i = -1;
170 if (!extension_id.empty())
171 query.BindString(++i, extension_id);
172 if (!api_name.empty())
173 query.BindString(++i, api_name);
174 if (type != Action::ACTION_ANY)
175 query.BindInt(++i, static_cast<int>(type));
176 if (!page_url.empty())
177 query.BindString(++i, page_url + "%");
178 if (!arg_url.empty())
179 query.BindString(++i, arg_url + "%");
180 if (days_ago >= 0) {
181 int64 early_bound;
182 int64 late_bound;
183 Util::ComputeDatabaseTimeBounds(Now(), days_ago, &early_bound, &late_bound);
184 query.BindInt64(++i, early_bound);
185 query.BindInt64(++i, late_bound);
188 // Execute the query and get results.
189 while (query.is_valid() && query.Step()) {
190 scoped_refptr<Action> action =
191 new Action(query.ColumnString(0),
192 base::Time::FromInternalValue(query.ColumnInt64(1)),
193 static_cast<Action::ActionType>(query.ColumnInt(2)),
194 query.ColumnString(3), query.ColumnInt64(9));
196 if (query.ColumnType(4) != sql::COLUMN_TYPE_NULL) {
197 scoped_ptr<base::Value> parsed_value(
198 base::JSONReader::DeprecatedRead(query.ColumnString(4)));
199 if (parsed_value && parsed_value->IsType(base::Value::TYPE_LIST)) {
200 action->set_args(make_scoped_ptr(
201 static_cast<base::ListValue*>(parsed_value.release())));
205 action->ParsePageUrl(query.ColumnString(5));
206 action->set_page_title(query.ColumnString(6));
207 action->ParseArgUrl(query.ColumnString(7));
209 if (query.ColumnType(8) != sql::COLUMN_TYPE_NULL) {
210 scoped_ptr<base::Value> parsed_value(
211 base::JSONReader::DeprecatedRead(query.ColumnString(8)));
212 if (parsed_value && parsed_value->IsType(base::Value::TYPE_DICTIONARY)) {
213 action->set_other(make_scoped_ptr(
214 static_cast<base::DictionaryValue*>(parsed_value.release())));
217 actions->push_back(action);
220 return actions.Pass();
223 void FullStreamUIPolicy::DoRemoveActions(const std::vector<int64>& action_ids) {
224 if (action_ids.empty())
225 return;
227 sql::Connection* db = GetDatabaseConnection();
228 if (!db) {
229 LOG(ERROR) << "Unable to connect to database";
230 return;
233 // Flush data first so the activity removal affects queued-up data as well.
234 activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
236 sql::Transaction transaction(db);
237 if (!transaction.Begin())
238 return;
240 std::string statement_str =
241 base::StringPrintf("DELETE FROM %s WHERE rowid = ?", kTableName);
242 sql::Statement statement(db->GetCachedStatement(
243 sql::StatementID(SQL_FROM_HERE), statement_str.c_str()));
244 for (size_t i = 0; i < action_ids.size(); i++) {
245 statement.Reset(true);
246 statement.BindInt64(0, action_ids[i]);
247 if (!statement.Run()) {
248 LOG(ERROR) << "Removing activities from database failed: "
249 << statement.GetSQLStatement();
250 return;
254 if (!transaction.Commit()) {
255 LOG(ERROR) << "Removing activities from database failed";
259 void FullStreamUIPolicy::DoRemoveURLs(const std::vector<GURL>& restrict_urls) {
260 sql::Connection* db = GetDatabaseConnection();
261 if (!db) {
262 LOG(ERROR) << "Unable to connect to database";
263 return;
266 // Make sure any queued in memory are sent to the database before cleaning.
267 activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
269 // If no restrictions then then all URLs need to be removed.
270 if (restrict_urls.empty()) {
271 sql::Statement statement;
272 std::string sql_str = base::StringPrintf(
273 "UPDATE %s SET page_url=NULL,page_title=NULL,arg_url=NULL",
274 kTableName);
275 statement.Assign(db->GetCachedStatement(
276 sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
278 if (!statement.Run()) {
279 LOG(ERROR) << "Removing URLs from database failed: "
280 << statement.GetSQLStatement();
282 return;
285 // If URLs are specified then restrict to only those URLs.
286 for (size_t i = 0; i < restrict_urls.size(); ++i) {
287 if (!restrict_urls[i].is_valid()) {
288 continue;
291 // Remove any matching page url info.
292 sql::Statement statement;
293 std::string sql_str = base::StringPrintf(
294 "UPDATE %s SET page_url=NULL,page_title=NULL WHERE page_url=?",
295 kTableName);
296 statement.Assign(db->GetCachedStatement(
297 sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
298 statement.BindString(0, restrict_urls[i].spec());
300 if (!statement.Run()) {
301 LOG(ERROR) << "Removing page URL from database failed: "
302 << statement.GetSQLStatement();
303 return;
306 // Remove any matching arg urls.
307 sql_str = base::StringPrintf("UPDATE %s SET arg_url=NULL WHERE arg_url=?",
308 kTableName);
309 statement.Assign(db->GetCachedStatement(
310 sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
311 statement.BindString(0, restrict_urls[i].spec());
313 if (!statement.Run()) {
314 LOG(ERROR) << "Removing arg URL from database failed: "
315 << statement.GetSQLStatement();
316 return;
321 void FullStreamUIPolicy::DoRemoveExtensionData(
322 const std::string& extension_id) {
323 if (extension_id.empty())
324 return;
326 sql::Connection* db = GetDatabaseConnection();
327 if (!db) {
328 LOG(ERROR) << "Unable to connect to database";
329 return;
332 // Make sure any queued in memory are sent to the database before cleaning.
333 activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
335 std::string sql_str = base::StringPrintf(
336 "DELETE FROM %s WHERE extension_id=?", kTableName);
337 sql::Statement statement;
338 statement.Assign(
339 db->GetCachedStatement(sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
340 statement.BindString(0, extension_id);
341 if (!statement.Run()) {
342 LOG(ERROR) << "Removing URLs for extension "
343 << extension_id << "from database failed: "
344 << statement.GetSQLStatement();
348 void FullStreamUIPolicy::DoDeleteDatabase() {
349 sql::Connection* db = GetDatabaseConnection();
350 if (!db) {
351 LOG(ERROR) << "Unable to connect to database";
352 return;
355 queued_actions_.clear();
357 // Not wrapped in a transaction because the deletion should happen even if
358 // the vacuuming fails.
359 std::string sql_str = base::StringPrintf("DELETE FROM %s;", kTableName);
360 sql::Statement statement(db->GetCachedStatement(
361 sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
362 if (!statement.Run()) {
363 LOG(ERROR) << "Deleting the database failed: "
364 << statement.GetSQLStatement();
365 return;
367 statement.Clear();
368 statement.Assign(db->GetCachedStatement(sql::StatementID(SQL_FROM_HERE),
369 "VACUUM"));
370 if (!statement.Run()) {
371 LOG(ERROR) << "Vacuuming the database failed: "
372 << statement.GetSQLStatement();
376 void FullStreamUIPolicy::OnDatabaseFailure() {
377 queued_actions_.clear();
380 void FullStreamUIPolicy::OnDatabaseClose() {
381 delete this;
384 void FullStreamUIPolicy::Close() {
385 // The policy object should have never been created if there's no DB thread.
386 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB));
387 ScheduleAndForget(activity_database(), &ActivityDatabase::Close);
390 void FullStreamUIPolicy::ReadFilteredData(
391 const std::string& extension_id,
392 const Action::ActionType type,
393 const std::string& api_name,
394 const std::string& page_url,
395 const std::string& arg_url,
396 const int days_ago,
397 const base::Callback
398 <void(scoped_ptr<Action::ActionVector>)>& callback) {
399 BrowserThread::PostTaskAndReplyWithResult(
400 BrowserThread::DB,
401 FROM_HERE,
402 base::Bind(&FullStreamUIPolicy::DoReadFilteredData,
403 base::Unretained(this),
404 extension_id,
405 type,
406 api_name,
407 page_url,
408 arg_url,
409 days_ago),
410 callback);
413 void FullStreamUIPolicy::RemoveActions(const std::vector<int64>& action_ids) {
414 ScheduleAndForget(this, &FullStreamUIPolicy::DoRemoveActions, action_ids);
417 void FullStreamUIPolicy::RemoveURLs(const std::vector<GURL>& restrict_urls) {
418 ScheduleAndForget(this, &FullStreamUIPolicy::DoRemoveURLs, restrict_urls);
421 void FullStreamUIPolicy::RemoveExtensionData(const std::string& extension_id) {
422 ScheduleAndForget(
423 this, &FullStreamUIPolicy::DoRemoveExtensionData, extension_id);
426 void FullStreamUIPolicy::DeleteDatabase() {
427 ScheduleAndForget(this, &FullStreamUIPolicy::DoDeleteDatabase);
430 scoped_refptr<Action> FullStreamUIPolicy::ProcessArguments(
431 scoped_refptr<Action> action) const {
432 return action;
435 void FullStreamUIPolicy::ProcessAction(scoped_refptr<Action> action) {
436 // TODO(mvrable): Right now this argument stripping updates the Action object
437 // in place, which isn't good if there are other users of the object. When
438 // database writing is moved to policy class, the modifications should be
439 // made locally.
440 action = ProcessArguments(action);
441 ScheduleAndForget(this, &FullStreamUIPolicy::QueueAction, action);
444 void FullStreamUIPolicy::QueueAction(scoped_refptr<Action> action) {
445 if (activity_database()->is_db_valid()) {
446 queued_actions_.push_back(action);
447 activity_database()->AdviseFlush(queued_actions_.size());
451 } // namespace extensions