| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" | 5 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" |
| 6 | 6 |
| 7 #include "base/callback.h" | 7 #include "base/callback.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
| 10 #include "base/json/json_reader.h" | 10 #include "base/json/json_reader.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 #include "url/gurl.h" | 24 #include "url/gurl.h" |
| 25 | 25 |
| 26 using base::Callback; | 26 using base::Callback; |
| 27 using base::FilePath; | 27 using base::FilePath; |
| 28 using base::Time; | 28 using base::Time; |
| 29 using base::Unretained; | 29 using base::Unretained; |
| 30 using content::BrowserThread; | 30 using content::BrowserThread; |
| 31 | 31 |
| 32 namespace constants = activity_log_constants; | 32 namespace constants = activity_log_constants; |
| 33 | 33 |
| 34 namespace { | |
| 35 | |
| 36 // Key strings for passing parameters to the ProcessAction member function. | |
| 37 const char kKeyReason[] = "fsuip.reason"; | |
| 38 const char kKeyDomainAction[] = "fsuip.domact"; | |
| 39 const char kKeyURLTitle[] = "fsuip.urltitle"; | |
| 40 const char kKeyDetailsString[] = "fsuip.details"; | |
| 41 | |
| 42 // Obsolete database tables: these should be dropped from the database if | |
| 43 // found. | |
| 44 const char* kObsoleteTables[] = {"activitylog_apis", "activitylog_blocked", | |
| 45 "activitylog_urls"}; | |
| 46 | |
| 47 std::string Serialize(const base::Value* value) { | |
| 48 std::string value_as_text; | |
| 49 if (!value) { | |
| 50 value_as_text = "null"; | |
| 51 } else { | |
| 52 JSONStringValueSerializer serializer(&value_as_text); | |
| 53 serializer.SerializeAndOmitBinaryValues(*value); | |
| 54 } | |
| 55 return value_as_text; | |
| 56 } | |
| 57 | |
| 58 } // namespace | |
| 59 | |
| 60 namespace extensions { | 34 namespace extensions { |
| 61 | 35 |
| 62 const char* FullStreamUIPolicy::kTableName = "activitylog_full"; | 36 const char* FullStreamUIPolicy::kTableName = "activitylog_full"; |
| 63 const char* FullStreamUIPolicy::kTableContentFields[] = { | 37 const char* FullStreamUIPolicy::kTableContentFields[] = { |
| 64 "extension_id", "time", "action_type", "api_name", "args", "page_url", | 38 "extension_id", "time", "action_type", "api_name", "args", "page_url", |
| 65 "page_title", "arg_url", "other" | 39 "page_title", "arg_url", "other" |
| 66 }; | 40 }; |
| 67 const char* FullStreamUIPolicy::kTableFieldTypes[] = { | 41 const char* FullStreamUIPolicy::kTableFieldTypes[] = { |
| 68 "LONGVARCHAR NOT NULL", "INTEGER", "INTEGER", "LONGVARCHAR", "LONGVARCHAR", | 42 "LONGVARCHAR NOT NULL", "INTEGER", "INTEGER", "LONGVARCHAR", "LONGVARCHAR", |
| 69 "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR" | 43 "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR", "LONGVARCHAR" |
| 70 }; | 44 }; |
| 71 const int FullStreamUIPolicy::kTableFieldCount = arraysize(kTableContentFields); | 45 const int FullStreamUIPolicy::kTableFieldCount = |
| 46 arraysize(FullStreamUIPolicy::kTableContentFields); |
| 72 | 47 |
| 73 FullStreamUIPolicy::FullStreamUIPolicy(Profile* profile) | 48 FullStreamUIPolicy::FullStreamUIPolicy(Profile* profile) |
| 74 : ActivityLogDatabasePolicy( | 49 : ActivityLogDatabasePolicy( |
| 75 profile, | 50 profile, |
| 76 FilePath(chrome::kExtensionActivityLogFilename)) {} | 51 FilePath(chrome::kExtensionActivityLogFilename)) {} |
| 77 | 52 |
| 78 FullStreamUIPolicy::~FullStreamUIPolicy() {} | 53 FullStreamUIPolicy::~FullStreamUIPolicy() {} |
| 79 | 54 |
| 80 bool FullStreamUIPolicy::InitDatabase(sql::Connection* db) { | 55 bool FullStreamUIPolicy::InitDatabase(sql::Connection* db) { |
| 81 // Drop old database tables. | 56 if (!Util::DropObsoleteTables(db)) |
| 82 for (size_t i = 0; i < arraysize(kObsoleteTables); i++) { | 57 return false; |
| 83 const char* table_name = kObsoleteTables[i]; | |
| 84 if (db->DoesTableExist(table_name)) { | |
| 85 std::string drop_statement = | |
| 86 base::StringPrintf("DROP TABLE %s", table_name); | |
| 87 if (!db->Execute(drop_statement.c_str())) { | |
| 88 return false; | |
| 89 } | |
| 90 } | |
| 91 } | |
| 92 | 58 |
| 93 // Create the unified activity log entry table. | 59 // Create the unified activity log entry table. |
| 94 return ActivityDatabase::InitializeTable(db, | 60 return ActivityDatabase::InitializeTable(db, |
| 95 kTableName, | 61 kTableName, |
| 96 kTableContentFields, | 62 kTableContentFields, |
| 97 kTableFieldTypes, | 63 kTableFieldTypes, |
| 98 arraysize(kTableContentFields)); | 64 arraysize(kTableContentFields)); |
| 99 } | 65 } |
| 100 | 66 |
| 101 bool FullStreamUIPolicy::FlushDatabase(sql::Connection* db) { | 67 bool FullStreamUIPolicy::FlushDatabase(sql::Connection* db) { |
| 102 if (queued_actions_.empty()) | 68 if (queued_actions_.empty()) |
| 103 return true; | 69 return true; |
| 104 | 70 |
| 105 sql::Transaction transaction(db); | 71 sql::Transaction transaction(db); |
| 106 if (!transaction.Begin()) | 72 if (!transaction.Begin()) |
| 107 return false; | 73 return false; |
| 108 | 74 |
| 109 std::string sql_str = | 75 std::string sql_str = |
| 110 "INSERT INTO " + std::string(FullStreamUIPolicy::kTableName) + | 76 "INSERT INTO " + std::string(FullStreamUIPolicy::kTableName) + |
| 111 " (extension_id, time, action_type, api_name, args, " | 77 " (extension_id, time, action_type, api_name, args, " |
| 112 "page_url, page_title, arg_url, other) VALUES (?,?,?,?,?,?,?,?,?)"; | 78 "page_url, page_title, arg_url, other) VALUES (?,?,?,?,?,?,?,?,?)"; |
| 113 sql::Statement statement(db->GetCachedStatement( | 79 sql::Statement statement(db->GetCachedStatement( |
| 114 sql::StatementID(SQL_FROM_HERE), sql_str.c_str())); | 80 sql::StatementID(SQL_FROM_HERE), sql_str.c_str())); |
| 115 | 81 |
| 116 url_canon::Replacements<char> url_sanitizer; | |
| 117 if (!CommandLine::ForCurrentProcess()->HasSwitch( | |
| 118 switches::kEnableExtensionActivityLogTesting)) { | |
| 119 url_sanitizer.ClearQuery(); | |
| 120 url_sanitizer.ClearRef(); | |
| 121 } | |
| 122 | |
| 123 Action::ActionVector::size_type i; | 82 Action::ActionVector::size_type i; |
| 124 for (i = 0; i != queued_actions_.size(); ++i) { | 83 for (i = 0; i != queued_actions_.size(); ++i) { |
| 125 const Action& action = *queued_actions_[i]; | 84 const Action& action = *queued_actions_[i]; |
| 126 statement.Reset(true); | 85 statement.Reset(true); |
| 127 statement.BindString(0, action.extension_id()); | 86 statement.BindString(0, action.extension_id()); |
| 128 statement.BindInt64(1, action.time().ToInternalValue()); | 87 statement.BindInt64(1, action.time().ToInternalValue()); |
| 129 statement.BindInt(2, static_cast<int>(action.action_type())); | 88 statement.BindInt(2, static_cast<int>(action.action_type())); |
| 130 statement.BindString(3, action.api_name()); | 89 statement.BindString(3, action.api_name()); |
| 131 if (action.args()) { | 90 if (action.args()) { |
| 132 statement.BindString(4, Serialize(action.args())); | 91 statement.BindString(4, Util::Serialize(action.args())); |
| 133 } | 92 } |
| 134 if (action.page_url().is_valid()) { | 93 std::string page_url_string = action.SerializePageUrl(); |
| 135 if (action.page_incognito()) { | 94 if (!page_url_string.empty()) { |
| 136 statement.BindString(5, constants::kIncognitoUrl); | 95 statement.BindString(5, page_url_string); |
| 137 } else { | |
| 138 statement.BindString( | |
| 139 5, action.page_url().ReplaceComponents(url_sanitizer).spec()); | |
| 140 } | |
| 141 } | 96 } |
| 142 if (!action.page_title().empty() && !action.page_incognito()) { | 97 if (!action.page_title().empty()) { |
| 143 statement.BindString(6, action.page_title()); | 98 statement.BindString(6, action.page_title()); |
| 144 } | 99 } |
| 145 if (action.arg_url().is_valid()) { | 100 std::string arg_url_string = action.SerializePageUrl(); |
| 146 if (action.arg_incognito()) { | 101 if (!arg_url_string.empty()) { |
| 147 statement.BindString(7, constants::kIncognitoUrl); | 102 statement.BindString(5, arg_url_string); |
| 148 } else { | |
| 149 statement.BindString( | |
| 150 7, action.arg_url().ReplaceComponents(url_sanitizer).spec()); | |
| 151 } | |
| 152 } | 103 } |
| 153 if (action.other()) { | 104 if (action.other()) { |
| 154 statement.BindString(8, Serialize(action.other())); | 105 statement.BindString(8, Util::Serialize(action.other())); |
| 155 } | 106 } |
| 156 | 107 |
| 157 if (!statement.Run()) { | 108 if (!statement.Run()) { |
| 158 LOG(ERROR) << "Activity log database I/O failed: " << sql_str; | 109 LOG(ERROR) << "Activity log database I/O failed: " << sql_str; |
| 159 return false; | 110 return false; |
| 160 } | 111 } |
| 161 } | 112 } |
| 162 | 113 |
| 163 if (!transaction.Commit()) | 114 if (!transaction.Commit()) |
| 164 return false; | 115 return false; |
| 165 | 116 |
| 166 queued_actions_.clear(); | 117 queued_actions_.clear(); |
| 167 return true; | 118 return true; |
| 168 } | 119 } |
| 169 | 120 |
| 170 scoped_ptr<Action::ActionVector> FullStreamUIPolicy::DoReadData( | 121 scoped_ptr<Action::ActionVector> FullStreamUIPolicy::DoReadData( |
| 171 const std::string& extension_id, | 122 const std::string& extension_id, |
| 172 const int days_ago) { | 123 const int days_ago) { |
| 124 // Ensure data is flushed to the database first so that we query over all |
| 125 // data. |
| 126 activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately); |
| 127 |
| 173 DCHECK_GE(days_ago, 0); | 128 DCHECK_GE(days_ago, 0); |
| 174 scoped_ptr<Action::ActionVector> actions(new Action::ActionVector()); | 129 scoped_ptr<Action::ActionVector> actions(new Action::ActionVector()); |
| 175 | 130 |
| 176 sql::Connection* db = GetDatabaseConnection(); | 131 sql::Connection* db = GetDatabaseConnection(); |
| 177 if (!db) { | 132 if (!db) { |
| 178 return actions.Pass(); | 133 return actions.Pass(); |
| 179 } | 134 } |
| 180 | 135 |
| 181 // Compute the time bounds for that day. | 136 int64 early_bound; |
| 182 base::Time morning_midnight = Now().LocalMidnight(); | 137 int64 late_bound; |
| 183 int64 early_bound = 0; | 138 Util::ComputeDatabaseTimeBounds(Now(), days_ago, &early_bound, &late_bound); |
| 184 int64 late_bound = 0; | |
| 185 if (days_ago == 0) { | |
| 186 early_bound = morning_midnight.ToInternalValue(); | |
| 187 late_bound = base::Time::Max().ToInternalValue(); | |
| 188 } else { | |
| 189 base::Time early_time = morning_midnight - | |
| 190 base::TimeDelta::FromDays(days_ago); | |
| 191 base::Time late_time = morning_midnight - | |
| 192 base::TimeDelta::FromDays(days_ago-1); | |
| 193 early_bound = early_time.ToInternalValue(); | |
| 194 late_bound = late_time.ToInternalValue(); | |
| 195 } | |
| 196 std::string query_str = base::StringPrintf( | 139 std::string query_str = base::StringPrintf( |
| 197 "SELECT time, action_type, api_name, args, page_url, page_title, " | 140 "SELECT time, action_type, api_name, args, page_url, page_title, " |
| 198 "arg_url, other " | 141 "arg_url, other " |
| 199 "FROM %s WHERE extension_id=? AND time>? AND time<=? " | 142 "FROM %s WHERE extension_id=? AND time>? AND time<=? " |
| 200 "ORDER BY time DESC", | 143 "ORDER BY time DESC", |
| 201 kTableName); | 144 kTableName); |
| 202 sql::Statement query(db->GetCachedStatement(SQL_FROM_HERE, | 145 sql::Statement query(db->GetCachedStatement(SQL_FROM_HERE, |
| 203 query_str.c_str())); | 146 query_str.c_str())); |
| 204 query.BindString(0, extension_id); | 147 query.BindString(0, extension_id); |
| 205 query.BindInt64(1, early_bound); | 148 query.BindInt64(1, early_bound); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 216 base::JSONReader::Read(query.ColumnString(3))); | 159 base::JSONReader::Read(query.ColumnString(3))); |
| 217 if (parsed_value && parsed_value->IsType(Value::TYPE_LIST)) { | 160 if (parsed_value && parsed_value->IsType(Value::TYPE_LIST)) { |
| 218 action->set_args( | 161 action->set_args( |
| 219 make_scoped_ptr(static_cast<ListValue*>(parsed_value.release()))); | 162 make_scoped_ptr(static_cast<ListValue*>(parsed_value.release()))); |
| 220 } else { | 163 } else { |
| 221 LOG(WARNING) << "Unable to parse args: '" << query.ColumnString(3) | 164 LOG(WARNING) << "Unable to parse args: '" << query.ColumnString(3) |
| 222 << "'"; | 165 << "'"; |
| 223 } | 166 } |
| 224 } | 167 } |
| 225 | 168 |
| 226 GURL page_url(query.ColumnString(4)); | 169 action->ParsePageUrl(query.ColumnString(4)); |
| 227 action->set_page_url(page_url); | |
| 228 | |
| 229 action->set_page_title(query.ColumnString(5)); | 170 action->set_page_title(query.ColumnString(5)); |
| 230 | 171 action->ParseArgUrl(query.ColumnString(6)); |
| 231 GURL arg_url(query.ColumnString(6)); | |
| 232 action->set_arg_url(arg_url); | |
| 233 | 172 |
| 234 if (query.ColumnType(7) != sql::COLUMN_TYPE_NULL) { | 173 if (query.ColumnType(7) != sql::COLUMN_TYPE_NULL) { |
| 235 scoped_ptr<Value> parsed_value( | 174 scoped_ptr<Value> parsed_value( |
| 236 base::JSONReader::Read(query.ColumnString(7))); | 175 base::JSONReader::Read(query.ColumnString(7))); |
| 237 if (parsed_value && parsed_value->IsType(Value::TYPE_DICTIONARY)) { | 176 if (parsed_value && parsed_value->IsType(Value::TYPE_DICTIONARY)) { |
| 238 action->set_other(make_scoped_ptr( | 177 action->set_other(make_scoped_ptr( |
| 239 static_cast<DictionaryValue*>(parsed_value.release()))); | 178 static_cast<DictionaryValue*>(parsed_value.release()))); |
| 240 } else { | 179 } else { |
| 241 LOG(WARNING) << "Unable to parse other: '" << query.ColumnString(7) | 180 LOG(WARNING) << "Unable to parse other: '" << query.ColumnString(7) |
| 242 << "'"; | 181 << "'"; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 270 BrowserThread::PostTaskAndReplyWithResult( | 209 BrowserThread::PostTaskAndReplyWithResult( |
| 271 BrowserThread::DB, | 210 BrowserThread::DB, |
| 272 FROM_HERE, | 211 FROM_HERE, |
| 273 base::Bind(&FullStreamUIPolicy::DoReadData, | 212 base::Bind(&FullStreamUIPolicy::DoReadData, |
| 274 base::Unretained(this), | 213 base::Unretained(this), |
| 275 extension_id, | 214 extension_id, |
| 276 day), | 215 day), |
| 277 callback); | 216 callback); |
| 278 } | 217 } |
| 279 | 218 |
| 280 std::string FullStreamUIPolicy::GetKey(ActivityLogPolicy::KeyType key_ty) const | |
| 281 { | |
| 282 switch (key_ty) { | |
| 283 case PARAM_KEY_REASON: | |
| 284 return std::string(kKeyReason); | |
| 285 case PARAM_KEY_DOM_ACTION: | |
| 286 return std::string(kKeyDomainAction); | |
| 287 case PARAM_KEY_URL_TITLE: | |
| 288 return std::string(kKeyURLTitle); | |
| 289 case PARAM_KEY_DETAILS_STRING: | |
| 290 return std::string(kKeyDetailsString); | |
| 291 default: | |
| 292 return std::string(); | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 scoped_refptr<Action> FullStreamUIPolicy::ProcessArguments( | 219 scoped_refptr<Action> FullStreamUIPolicy::ProcessArguments( |
| 297 scoped_refptr<Action> action) const { | 220 scoped_refptr<Action> action) const { |
| 298 return action; | 221 return action; |
| 299 } | 222 } |
| 300 | 223 |
| 301 void FullStreamUIPolicy::ProcessAction(scoped_refptr<Action> action) { | 224 void FullStreamUIPolicy::ProcessAction(scoped_refptr<Action> action) { |
| 302 // TODO(mvrable): Right now this argument stripping updates the Action object | 225 // TODO(mvrable): Right now this argument stripping updates the Action object |
| 303 // in place, which isn't good if there are other users of the object. When | 226 // in place, which isn't good if there are other users of the object. When |
| 304 // database writing is moved to policy class, the modifications should be | 227 // database writing is moved to policy class, the modifications should be |
| 305 // made locally. | 228 // made locally. |
| 306 action = ProcessArguments(action); | 229 action = ProcessArguments(action); |
| 307 ScheduleAndForget(this, &FullStreamUIPolicy::QueueAction, action); | 230 ScheduleAndForget(this, &FullStreamUIPolicy::QueueAction, action); |
| 308 } | 231 } |
| 309 | 232 |
| 310 void FullStreamUIPolicy::QueueAction(scoped_refptr<Action> action) { | 233 void FullStreamUIPolicy::QueueAction(scoped_refptr<Action> action) { |
| 311 if (activity_database()->is_db_valid()) { | 234 if (activity_database()->is_db_valid()) { |
| 312 queued_actions_.push_back(action); | 235 queued_actions_.push_back(action); |
| 313 activity_database()->NotifyAction(); | 236 activity_database()->AdviseFlush(queued_actions_.size()); |
| 314 } | 237 } |
| 315 } | 238 } |
| 316 | 239 |
| 317 } // namespace extensions | 240 } // namespace extensions |
| OLD | NEW |