Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: chrome/browser/extensions/activity_log/activity_actions.cc

Issue 19690003: Extension activity log database refactoring (step 3) (Closed) Base URL: http://git.chromium.org/chromium/src.git@refactor2
Patch Set: Rebase Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 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 <string> 5 #include <string>
6 #include "base/command_line.h"
6 #include "base/json/json_string_value_serializer.h" 7 #include "base/json/json_string_value_serializer.h"
7 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
9 #include "chrome/browser/extensions/activity_log/activity_actions.h" 11 #include "chrome/browser/extensions/activity_log/activity_actions.h"
12 #include "chrome/browser/extensions/activity_log/api_name_constants.h"
13 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
14 #include "chrome/browser/extensions/extension_tab_util.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/web_contents.h"
19 #include "sql/statement.h"
10 20
11 namespace { 21 namespace {
12 22
13 std::string Serialize(const base::Value* value) { 23 std::string Serialize(const base::Value* value) {
14 std::string value_as_text; 24 std::string value_as_text;
15 if (!value) { 25 if (!value) {
16 value_as_text = "null"; 26 value_as_text = "null";
17 } else { 27 } else {
18 JSONStringValueSerializer serializer(&value_as_text); 28 JSONStringValueSerializer serializer(&value_as_text);
19 serializer.SerializeAndOmitBinaryValues(*value); 29 serializer.SerializeAndOmitBinaryValues(*value);
20 } 30 }
21 return value_as_text; 31 return value_as_text;
22 } 32 }
23 33
34 // Gets the URL for a given tab ID. Helper method for APIAction::LookupTabId.
35 std::string GetURLForTabId(const int tab_id, Profile* profile) {
36 content::WebContents* contents = NULL;
37 Browser* browser = NULL;
38 bool found = ExtensionTabUtil::GetTabById(tab_id,
39 profile,
40 true, // search incognito tabs too
41 &browser,
42 NULL,
43 &contents,
44 NULL);
45 if (found) {
46 // Check whether the profile the tab was found in is a normal or incognito
47 // profile.
48 if (!browser->profile()->IsOffTheRecord()) {
49 GURL url = contents->GetURL();
50 return std::string(url.spec());
51 } else {
52 return std::string(extensions::APIAction::kIncognitoUrl);
53 }
54 } else {
55 return std::string();
56 }
57 }
58
59 // Sets up the hashmap for mapping extension strings to "ints". The hashmap is
60 // only set up once because it's quite long; the value is then cached.
61 class APINameMap {
62 public:
63 APINameMap() {
64 SetupMap();
65 }
66
67 // activity_log_api_name_constants.h lists all known API calls as of 5/17.
68 // This code maps each of those API calls (and events) to short strings
69 // (integers converted to strings). They're all strings because (1) sqlite
70 // databases are all strings underneath anyway and (2) the Lookup function
71 // will simply return the original api_call string if we don't have it in our
72 // lookup table.
73 void SetupMap() {
74 for (size_t i = 0;
75 i < arraysize(activity_log_api_name_constants::kNames);
76 ++i) {
77 std::string name =
78 std::string(activity_log_api_name_constants::kNames[i]);
79 std::string num = base::IntToString(i);
80 names_to_nums_[name] = num;
81 nums_to_names_[num] = name;
82 }
83 }
84
85 static APINameMap* GetInstance() {
86 return Singleton<APINameMap>::get();
87 }
88
89 // This matches an api call to a number, if it's in the lookup table. If not,
90 // it returns the original api call.
91 const std::string& ApiToShortname(const std::string& api_call) {
92 std::map<std::string, std::string>::iterator it =
93 names_to_nums_.find(api_call);
94 if (it == names_to_nums_.end())
95 return api_call;
96 else
97 return it->second;
98 }
99
100 // This matches a number to an API call -- it's the opposite of
101 // ApiToShortname.
102 const std::string& ShortnameToApi(const std::string& shortname) {
103 std::map<std::string, std::string>::iterator it =
104 nums_to_names_.find(shortname);
105 if (it == nums_to_names_.end())
106 return shortname;
107 else
108 return it->second;
109 }
110
111 private:
112 std::map<std::string, std::string> names_to_nums_; // <name, number label>
113 std::map<std::string, std::string> nums_to_names_; // <number label, name>
114 };
115
24 } // namespace 116 } // namespace
25 117
26 namespace extensions { 118 namespace extensions {
27 119
120 using api::activity_log_private::BlockedChromeActivityDetail;
121 using api::activity_log_private::ChromeActivityDetail;
122 using api::activity_log_private::DomActivityDetail;
28 using api::activity_log_private::ExtensionActivity; 123 using api::activity_log_private::ExtensionActivity;
29 124
125 // We should log the arguments to these API calls, even if argument logging is
126 // disabled by default.
127 const char* APIAction::kAlwaysLog[] =
128 {"extension.connect", "extension.sendMessage",
129 "tabs.executeScript", "tabs.insertCSS" };
130 const int APIAction::kSizeAlwaysLog = arraysize(kAlwaysLog);
131
132 // A string used in place of the real URL when the URL is hidden because it is
133 // in an incognito window. Extension activity logs mentioning kIncognitoUrl
134 // let the user know that an extension is manipulating incognito tabs without
135 // recording specific data about the pages.
136 const char* APIAction::kIncognitoUrl = "http://incognito/";
137
138 // static
139 void APIAction::LookupTabId(const std::string& api_call,
140 base::ListValue* args,
141 Profile* profile) {
142 if (api_call == "tabs.get" || // api calls, ID as int
143 api_call == "tabs.connect" ||
144 api_call == "tabs.sendMessage" ||
145 api_call == "tabs.duplicate" ||
146 api_call == "tabs.update" ||
147 api_call == "tabs.reload" ||
148 api_call == "tabs.detectLanguage" ||
149 api_call == "tabs.executeScript" ||
150 api_call == "tabs.insertCSS" ||
151 api_call == "tabs.move" || // api calls, IDs in array
152 api_call == "tabs.remove" ||
153 api_call == "tabs.onUpdated" || // events, ID as int
154 api_call == "tabs.onMoved" ||
155 api_call == "tabs.onDetached" ||
156 api_call == "tabs.onAttached" ||
157 api_call == "tabs.onRemoved" ||
158 api_call == "tabs.onReplaced") {
159 int tab_id;
160 base::ListValue* id_list;
161 if (args->GetInteger(0, &tab_id)) {
162 std::string url = GetURLForTabId(tab_id, profile);
163 if (url != std::string())
164 args->Set(0, new base::StringValue(url));
165 } else if ((api_call == "tabs.move" || api_call == "tabs.remove") &&
166 args->GetList(0, &id_list)) {
167 for (int i = 0; i < static_cast<int>(id_list->GetSize()); ++i) {
168 if (id_list->GetInteger(i, &tab_id)) {
169 std::string url = GetURLForTabId(tab_id, profile);
170 if (url != std::string())
171 id_list->Set(i, new base::StringValue(url));
172 } else {
173 LOG(ERROR) << "The tab ID array is malformed at index " << i;
174 }
175 }
176 }
177 }
178 }
179
30 Action::Action(const std::string& extension_id, 180 Action::Action(const std::string& extension_id,
31 const base::Time& time, 181 const base::Time& time,
32 ExtensionActivity::ActivityType activity_type) 182 const ActionType action_type)
33 : extension_id_(extension_id), 183 : extension_id_(extension_id), time_(time), action_type_(action_type) {}
34 time_(time), 184
35 activity_type_(activity_type) {} 185 Action::~Action() {}
36 186
37 WatchdogAction::WatchdogAction(const std::string& extension_id, 187 void Action::set_args(scoped_ptr<ListValue> args) {
38 const base::Time& time, 188 args_.reset(args.release());
39 const ActionType action_type, 189 }
40 const std::string& api_name, 190
41 scoped_ptr<ListValue> args, 191 ListValue* Action::mutable_args() {
42 const GURL& page_url, 192 if (!args_.get()) {
43 const GURL& arg_url, 193 args_.reset(new ListValue());
44 scoped_ptr<DictionaryValue> other) 194 }
45 : Action(extension_id, time, ExtensionActivity::ACTIVITY_TYPE_CHROME), 195 return args_.get();
46 action_type_(action_type), 196 }
47 api_name_(api_name), 197
48 args_(args.Pass()), 198 void Action::set_page_url(const GURL& page_url) {
49 page_url_(page_url), 199 page_url_ = page_url;
50 arg_url_(arg_url), 200 }
51 other_(other.Pass()) {} 201
52 202 void Action::set_arg_url(const GURL& arg_url) {
53 WatchdogAction::~WatchdogAction() {} 203 arg_url_ = arg_url;
54 204 }
55 bool WatchdogAction::Record(sql::Connection* db) { 205
56 // This methods isn't used and will go away entirely soon once database 206 void Action::set_other(scoped_ptr<DictionaryValue> other) {
57 // writing moves to the policy objects. 207 other_.reset(other.release());
58 NOTREACHED(); 208 }
209
210 DictionaryValue* Action::mutable_other() {
211 if (!other_.get()) {
212 other_.reset(new DictionaryValue());
213 }
214 return other_.get();
215 }
216
217 bool Action::Record(sql::Connection* db) {
218 std::string sql_str =
219 "INSERT INTO " + std::string(FullStreamUIPolicy::kTableName) +
220 " (extension_id, time, action_type, api_name, args, "
221 "page_url, page_title, arg_url, other) VALUES (?,?,?,?,?,?,?,?,?)";
222 sql::Statement statement(db->GetCachedStatement(
223 sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
224 statement.BindString(0, extension_id());
225 statement.BindInt64(1, time().ToInternalValue());
226 statement.BindInt(2, static_cast<int>(action_type()));
227 statement.BindString(3, api_name());
228 if (args()) {
229 statement.BindString(4, Serialize(args()));
230 } else {
231 statement.BindNull(4);
232 }
233 if (other()) {
234 statement.BindString(8, Serialize(other()));
235 } else {
236 statement.BindNull(8);
237 }
238
239 url_canon::Replacements<char> url_sanitizer;
240 if (!CommandLine::ForCurrentProcess()->HasSwitch(
241 switches::kEnableExtensionActivityLogTesting)) {
242 url_sanitizer.ClearQuery();
243 url_sanitizer.ClearRef();
244 }
245 if (page_url().is_valid()) {
246 statement.BindString(5, page_url().ReplaceComponents(url_sanitizer).spec());
247 }
248 statement.BindString(6, page_title());
249 if (arg_url().is_valid()) {
250 statement.BindString(7, arg_url().ReplaceComponents(url_sanitizer).spec());
251 }
252
253 if (!statement.Run()) {
254 LOG(ERROR) << "Activity log database I/O failed: " << sql_str;
255 statement.Clear();
256 return false;
257 }
59 return true; 258 return true;
60 } 259 }
61 260
62 scoped_ptr<api::activity_log_private::ExtensionActivity> 261 scoped_ptr<ExtensionActivity> Action::ConvertToExtensionActivity() {
63 WatchdogAction::ConvertToExtensionActivity() { 262 scoped_ptr<ExtensionActivity> result(new ExtensionActivity);
64 scoped_ptr<api::activity_log_private::ExtensionActivity> result; 263
264 result->extension_id.reset(new std::string(extension_id()));
265 result->time.reset(new double(time().ToJsTime()));
266
267 switch (action_type()) {
268 case ACTION_API_CALL:
269 case ACTION_API_EVENT: {
270 ChromeActivityDetail* details = new ChromeActivityDetail;
271 if (action_type() == ACTION_API_CALL) {
272 details->api_activity_type =
273 ChromeActivityDetail::API_ACTIVITY_TYPE_CALL;
274 } else {
275 details->api_activity_type =
276 ChromeActivityDetail::API_ACTIVITY_TYPE_EVENT_CALLBACK;
277 }
278 details->api_call.reset(new std::string(api_name()));
279 details->args.reset(new std::string(Serialize(args())));
280 details->extra.reset(new std::string(Serialize(other())));
281
282 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_CHROME;
283 result->chrome_activity_detail.reset(details);
284 break;
285 }
286
287 case ACTION_API_BLOCKED: {
288 BlockedChromeActivityDetail* details = new BlockedChromeActivityDetail;
289 details->api_call.reset(new std::string(api_name()));
290 details->args.reset(new std::string(Serialize(args())));
291 details->extra.reset(new std::string(Serialize(other())));
292 if (other()) {
293 int reason;
294 if (other()->GetInteger("reason", &reason)) {
295 details->reason =
296 static_cast<BlockedChromeActivityDetail::Reason>(reason);
297 }
298 }
299
300 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_BLOCKED_CHROME;
301 result->blocked_chrome_activity_detail.reset(details);
302 break;
303 }
304
305 case ACTION_DOM_EVENT:
306 case ACTION_DOM_XHR:
307 case ACTION_DOM_ACCESS:
308 case ACTION_CONTENT_SCRIPT:
309 case ACTION_WEB_REQUEST: {
310 DomActivityDetail* details = new DomActivityDetail;
311
312 if (action_type() == ACTION_WEB_REQUEST) {
313 details->dom_activity_type =
314 DomActivityDetail::DOM_ACTIVITY_TYPE_WEBREQUEST;
315 } else if (action_type() == ACTION_CONTENT_SCRIPT) {
316 details->dom_activity_type =
317 DomActivityDetail::DOM_ACTIVITY_TYPE_INSERTED;
318 } else {
319 // TODO(mvrable): This ought to be filled in properly, but since the
320 // API will change soon don't worry about it now.
321 details->dom_activity_type =
322 DomActivityDetail::DOM_ACTIVITY_TYPE_NONE;
323 }
324 details->api_call.reset(new std::string(api_name()));
325 details->args.reset(new std::string(Serialize(args())));
326 details->extra.reset(new std::string(Serialize(other())));
327 details->url.reset(new std::string(page_url().spec()));
328 if (!page_title().empty())
329 details->url_title.reset(new std::string(page_title()));
330
331 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_DOM;
332 result->dom_activity_detail.reset(details);
333 break;
334 }
335
336 default:
337 LOG(WARNING) << "Bad activity log entry read from database (type="
338 << action_type_ << ")!";
339 }
340
65 return result.Pass(); 341 return result.Pass();
66 } 342 }
67 343
68 std::string WatchdogAction::PrintForDebug() { 344 std::string Action::PrintForDebug() {
69 std::string result = "ID=" + extension_id() + " CATEGORY="; 345 std::string result = "ID=" + extension_id() + " CATEGORY=";
70 switch (action_type_) { 346 switch (action_type_) {
71 case ACTION_API_CALL: 347 case ACTION_API_CALL:
72 result += "api_call"; 348 result += "api_call";
73 break; 349 break;
74 case ACTION_API_EVENT: 350 case ACTION_API_EVENT:
75 result += "api_event_callback"; 351 result += "api_event_callback";
76 break; 352 break;
77 case ACTION_WEB_REQUEST: 353 case ACTION_WEB_REQUEST:
78 result += "webrequest"; 354 result += "webrequest";
(...skipping 17 matching lines...) Expand all
96 result += base::StringPrintf("type%d", static_cast<int>(action_type_)); 372 result += base::StringPrintf("type%d", static_cast<int>(action_type_));
97 } 373 }
98 374
99 result += " API=" + api_name_; 375 result += " API=" + api_name_;
100 if (args_.get()) { 376 if (args_.get()) {
101 result += " ARGS=" + Serialize(args_.get()); 377 result += " ARGS=" + Serialize(args_.get());
102 } 378 }
103 if (page_url_.is_valid()) { 379 if (page_url_.is_valid()) {
104 result += " PAGE_URL=" + page_url_.spec(); 380 result += " PAGE_URL=" + page_url_.spec();
105 } 381 }
382 if (!page_title_.empty()) {
383 StringValue title(page_title_);
384 result += " PAGE_TITLE=" + Serialize(&title);
385 }
106 if (arg_url_.is_valid()) { 386 if (arg_url_.is_valid()) {
107 result += " ARG_URL=" + arg_url_.spec(); 387 result += " ARG_URL=" + arg_url_.spec();
108 } 388 }
109 if (other_.get()) { 389 if (other_.get()) {
110 result += " OTHER=" + Serialize(other_.get()); 390 result += " OTHER=" + Serialize(other_.get());
111 } 391 }
112 392
113 return result; 393 return result;
114 } 394 }
115 395
116 } // namespace extensions 396 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698