Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / activity_log / fullstream_ui_policy.cc
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.
4
5 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
6
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 "chrome/common/extensions/dom_action_types.h"
21 #include "extensions/common/extension.h"
22 #include "sql/error_delegate_util.h"
23 #include "sql/transaction.h"
24 #include "url/gurl.h"
25
26 using base::Callback;
27 using base::FilePath;
28 using base::Time;
29 using base::Unretained;
30 using content::BrowserThread;
31
32 namespace constants = activity_log_constants;
33
34 namespace extensions {
35
36 const char* FullStreamUIPolicy::kTableName = "activitylog_full";
37 const char* FullStreamUIPolicy::kTableContentFields[] = {
38   "extension_id", "time", "action_type", "api_name", "args", "page_url",
39   "page_title", "arg_url", "other"
40 };
41 const char* FullStreamUIPolicy::kTableFieldTypes[] = {
42   "LONGVARCHAR NOT NULL", "INTEGER", "INTEGER", "LONGVARCHAR", "LONGVARCHAR",
43   "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR"
44 };
45 const int FullStreamUIPolicy::kTableFieldCount =
46     arraysize(FullStreamUIPolicy::kTableContentFields);
47
48 FullStreamUIPolicy::FullStreamUIPolicy(Profile* profile)
49     : ActivityLogDatabasePolicy(
50           profile,
51           FilePath(chrome::kExtensionActivityLogFilename)) {}
52
53 FullStreamUIPolicy::~FullStreamUIPolicy() {}
54
55 bool FullStreamUIPolicy::InitDatabase(sql::Connection* db) {
56   if (!Util::DropObsoleteTables(db))
57     return false;
58
59   // Create the unified activity log entry table.
60   return ActivityDatabase::InitializeTable(db,
61                                            kTableName,
62                                            kTableContentFields,
63                                            kTableFieldTypes,
64                                            arraysize(kTableContentFields));
65 }
66
67 bool FullStreamUIPolicy::FlushDatabase(sql::Connection* db) {
68   if (queued_actions_.empty())
69     return true;
70
71   sql::Transaction transaction(db);
72   if (!transaction.Begin())
73     return false;
74
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()));
81
82   Action::ActionVector::size_type i;
83   for (i = 0; i != queued_actions_.size(); ++i) {
84     const Action& action = *queued_actions_[i];
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()));
92     }
93     std::string page_url_string = action.SerializePageUrl();
94     if (!page_url_string.empty()) {
95       statement.BindString(5, page_url_string);
96     }
97     if (!action.page_title().empty()) {
98       statement.BindString(6, action.page_title());
99     }
100     std::string arg_url_string = action.SerializeArgUrl();
101     if (!arg_url_string.empty()) {
102       statement.BindString(7, arg_url_string);
103     }
104     if (action.other()) {
105       statement.BindString(8, Util::Serialize(action.other()));
106     }
107
108     if (!statement.Run()) {
109       LOG(ERROR) << "Activity log database I/O failed: " << sql_str;
110       return false;
111     }
112   }
113
114   if (!transaction.Commit())
115     return false;
116
117   queued_actions_.clear();
118   return true;
119 }
120
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());
132
133   sql::Connection* db = GetDatabaseConnection();
134   if (!db) {
135     return actions.Pass();
136   }
137
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 ";
144   }
145   if (!api_name.empty()) {
146     where_str += where_next + "api_name=?";
147     where_next = " AND ";
148   }
149   if (type != Action::ACTION_ANY) {
150     where_str += where_next + "action_type=?";
151     where_next = " AND ";
152   }
153   if (!page_url.empty()) {
154     where_str += where_next + "page_url LIKE ?";
155     where_next = " AND ";
156   }
157   if (!arg_url.empty()) {
158     where_str += where_next + "arg_url LIKE ?";
159   }
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);
186   }
187
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));
195
196     if (query.ColumnType(4) != sql::COLUMN_TYPE_NULL) {
197       scoped_ptr<base::Value> parsed_value(
198           base::JSONReader::Read(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())));
202       }
203     }
204
205     action->ParsePageUrl(query.ColumnString(5));
206     action->set_page_title(query.ColumnString(6));
207     action->ParseArgUrl(query.ColumnString(7));
208
209     if (query.ColumnType(8) != sql::COLUMN_TYPE_NULL) {
210       scoped_ptr<base::Value> parsed_value(
211           base::JSONReader::Read(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())));
215       }
216     }
217     actions->push_back(action);
218   }
219
220   return actions.Pass();
221 }
222
223 void FullStreamUIPolicy::DoRemoveActions(const std::vector<int64>& action_ids) {
224   if (action_ids.empty())
225     return;
226
227   sql::Connection* db = GetDatabaseConnection();
228   if (!db) {
229     LOG(ERROR) << "Unable to connect to database";
230     return;
231   }
232
233   // Flush data first so the activity removal affects queued-up data as well.
234   activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
235
236   sql::Transaction transaction(db);
237   if (!transaction.Begin())
238     return;
239
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;
251     }
252   }
253
254   if (!transaction.Commit()) {
255     LOG(ERROR) << "Removing activities from database failed";
256   }
257 }
258
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;
264   }
265
266   // Make sure any queued in memory are sent to the database before cleaning.
267   activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
268
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()));
277
278     if (!statement.Run()) {
279       LOG(ERROR) << "Removing URLs from database failed: "
280                  << statement.GetSQLStatement();
281     }
282     return;
283   }
284
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;
289     }
290
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());
299
300     if (!statement.Run()) {
301       LOG(ERROR) << "Removing page URL from database failed: "
302                  << statement.GetSQLStatement();
303       return;
304     }
305
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());
312
313     if (!statement.Run()) {
314       LOG(ERROR) << "Removing arg URL from database failed: "
315                  << statement.GetSQLStatement();
316       return;
317     }
318   }
319 }
320
321 void FullStreamUIPolicy::DoRemoveExtensionData(
322     const std::string& extension_id) {
323   if (extension_id.empty())
324     return;
325
326   sql::Connection* db = GetDatabaseConnection();
327   if (!db) {
328     LOG(ERROR) << "Unable to connect to database";
329     return;
330   }
331
332   // Make sure any queued in memory are sent to the database before cleaning.
333   activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
334
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();
345   }
346 }
347
348 void FullStreamUIPolicy::DoDeleteDatabase() {
349   sql::Connection* db = GetDatabaseConnection();
350   if (!db) {
351     LOG(ERROR) << "Unable to connect to database";
352     return;
353   }
354
355   queued_actions_.clear();
356
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;
366   }
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();
373   }
374 }
375
376 void FullStreamUIPolicy::OnDatabaseFailure() {
377   queued_actions_.clear();
378 }
379
380 void FullStreamUIPolicy::OnDatabaseClose() {
381   delete this;
382 }
383
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);
388 }
389
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);
411 }
412
413 void FullStreamUIPolicy::RemoveActions(const std::vector<int64>& action_ids) {
414   ScheduleAndForget(this, &FullStreamUIPolicy::DoRemoveActions, action_ids);
415 }
416
417 void FullStreamUIPolicy::RemoveURLs(const std::vector<GURL>& restrict_urls) {
418   ScheduleAndForget(this, &FullStreamUIPolicy::DoRemoveURLs, restrict_urls);
419 }
420
421 void FullStreamUIPolicy::RemoveExtensionData(const std::string& extension_id) {
422   ScheduleAndForget(
423       this, &FullStreamUIPolicy::DoRemoveExtensionData, extension_id);
424 }
425
426 void FullStreamUIPolicy::DeleteDatabase() {
427   ScheduleAndForget(this, &FullStreamUIPolicy::DoDeleteDatabase);
428 }
429
430 scoped_refptr<Action> FullStreamUIPolicy::ProcessArguments(
431     scoped_refptr<Action> action) const {
432   return action;
433 }
434
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);
442 }
443
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());
448   }
449 }
450
451 }  // namespace extensions