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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/activity_log/activity_actions.h"
9 #include "base/command_line.h"
10 #include "base/format_macros.h"
11 #include "base/json/json_string_value_serializer.h"
12 #include "base/logging.h"
13 #include "base/memory/singleton.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
18 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/dom_action_types.h"
22 #include "content/public/browser/web_contents.h"
23 #include "sql/statement.h"
25 namespace constants = activity_log_constants;
29 std::string Serialize(const base::Value* value) {
30 std::string value_as_text;
32 value_as_text = "null";
34 JSONStringValueSerializer serializer(&value_as_text);
35 serializer.SerializeAndOmitBinaryValues(*value);
42 namespace extensions {
44 using api::activity_log_private::ExtensionActivity;
46 Action::Action(const std::string& extension_id,
47 const base::Time& time,
48 const ActionType action_type,
49 const std::string& api_name,
51 : extension_id_(extension_id),
53 action_type_(action_type),
55 page_incognito_(false),
56 arg_incognito_(false),
58 action_id_(action_id) {}
62 // TODO(mvrable): As an optimization, we might return this directly if the
63 // refcount is one. However, there are likely to be other stray references in
64 // many cases that will prevent this optimization.
65 scoped_refptr<Action> Action::Clone() const {
66 scoped_refptr<Action> clone(
68 extension_id(), time(), action_type(), api_name(), action_id()));
70 clone->set_args(make_scoped_ptr(args()->DeepCopy()));
71 clone->set_page_url(page_url());
72 clone->set_page_title(page_title());
73 clone->set_page_incognito(page_incognito());
74 clone->set_arg_url(arg_url());
75 clone->set_arg_incognito(arg_incognito());
77 clone->set_other(make_scoped_ptr(other()->DeepCopy()));
81 void Action::set_args(scoped_ptr<base::ListValue> args) {
82 args_.reset(args.release());
85 base::ListValue* Action::mutable_args() {
87 args_.reset(new base::ListValue());
92 void Action::set_page_url(const GURL& page_url) {
96 void Action::set_arg_url(const GURL& arg_url) {
100 void Action::set_other(scoped_ptr<base::DictionaryValue> other) {
101 other_.reset(other.release());
104 base::DictionaryValue* Action::mutable_other() {
106 other_.reset(new base::DictionaryValue());
111 std::string Action::SerializePageUrl() const {
112 return (page_incognito() ? constants::kIncognitoUrl : "") + page_url().spec();
115 void Action::ParsePageUrl(const std::string& url) {
116 set_page_incognito(StartsWithASCII(url, constants::kIncognitoUrl, true));
117 if (page_incognito())
118 set_page_url(GURL(url.substr(strlen(constants::kIncognitoUrl))));
120 set_page_url(GURL(url));
123 std::string Action::SerializeArgUrl() const {
124 return (arg_incognito() ? constants::kIncognitoUrl : "") + arg_url().spec();
127 void Action::ParseArgUrl(const std::string& url) {
128 set_arg_incognito(StartsWithASCII(url, constants::kIncognitoUrl, true));
130 set_arg_url(GURL(url.substr(strlen(constants::kIncognitoUrl))));
132 set_arg_url(GURL(url));
135 scoped_ptr<ExtensionActivity> Action::ConvertToExtensionActivity() {
136 scoped_ptr<ExtensionActivity> result(new ExtensionActivity);
138 // We do this translation instead of using the same enum because the database
139 // values need to be stable; this allows us to change the extension API
140 // without affecting the database.
141 switch (action_type()) {
142 case ACTION_API_CALL:
143 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_API_CALL;
145 case ACTION_API_EVENT:
146 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_API_EVENT;
148 case ACTION_CONTENT_SCRIPT:
149 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_CONTENT_SCRIPT;
151 case ACTION_DOM_ACCESS:
152 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_DOM_ACCESS;
154 case ACTION_DOM_EVENT:
155 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_DOM_EVENT;
157 case ACTION_WEB_REQUEST:
158 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_WEB_REQUEST;
160 case UNUSED_ACTION_API_BLOCKED:
163 // This shouldn't be reached, but some people might have old or otherwise
164 // weird db entries. Treat it like an API call if that happens.
165 result->activity_type = ExtensionActivity::ACTIVITY_TYPE_API_CALL;
169 result->extension_id.reset(new std::string(extension_id()));
170 result->time.reset(new double(time().ToJsTime()));
171 result->count.reset(new double(count()));
172 result->api_call.reset(new std::string(api_name()));
173 result->args.reset(new std::string(Serialize(args())));
174 if (action_id() != -1)
175 result->activity_id.reset(
176 new std::string(base::StringPrintf("%" PRId64, action_id())));
177 if (page_url().is_valid()) {
178 if (!page_title().empty())
179 result->page_title.reset(new std::string(page_title()));
180 result->page_url.reset(new std::string(SerializePageUrl()));
182 if (arg_url().is_valid())
183 result->arg_url.reset(new std::string(SerializeArgUrl()));
186 scoped_ptr<ExtensionActivity::Other> other_field(
187 new ExtensionActivity::Other);
189 if (other()->GetBooleanWithoutPathExpansion(constants::kActionPrerender,
191 other_field->prerender.reset(new bool(prerender));
193 const base::DictionaryValue* web_request;
194 if (other()->GetDictionaryWithoutPathExpansion(constants::kActionWebRequest,
196 other_field->web_request.reset(new std::string(
197 ActivityLogPolicy::Util::Serialize(web_request)));
200 if (other()->GetStringWithoutPathExpansion(constants::kActionExtra, &extra))
201 other_field->extra.reset(new std::string(extra));
203 if (other()->GetIntegerWithoutPathExpansion(constants::kActionDomVerb,
205 switch (static_cast<DomActionType::Type>(dom_verb)) {
206 case DomActionType::GETTER:
207 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_GETTER;
209 case DomActionType::SETTER:
210 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_SETTER;
212 case DomActionType::METHOD:
213 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_METHOD;
215 case DomActionType::INSERTED:
216 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_INSERTED;
218 case DomActionType::XHR:
219 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_XHR;
221 case DomActionType::WEBREQUEST:
222 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_WEBREQUEST;
224 case DomActionType::MODIFIED:
225 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_MODIFIED;
228 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_NONE;
231 other_field->dom_verb = ExtensionActivity::Other::DOM_VERB_NONE;
233 result->other.reset(other_field.release());
236 return result.Pass();
239 std::string Action::PrintForDebug() const {
240 std::string result = base::StringPrintf("ACTION ID=%" PRId64, action_id());
241 result += " EXTENSION ID=" + extension_id() + " CATEGORY=";
242 switch (action_type_) {
243 case ACTION_API_CALL:
244 result += "api_call";
246 case ACTION_API_EVENT:
247 result += "api_event_callback";
249 case ACTION_WEB_REQUEST:
250 result += "webrequest";
252 case ACTION_CONTENT_SCRIPT:
253 result += "content_script";
255 case UNUSED_ACTION_API_BLOCKED:
256 // This is deprecated.
257 result += "api_blocked";
259 case ACTION_DOM_EVENT:
260 result += "dom_event";
262 case ACTION_DOM_ACCESS:
263 result += "dom_access";
266 result += base::StringPrintf("type%d", static_cast<int>(action_type_));
269 result += " API=" + api_name_;
271 result += " ARGS=" + Serialize(args_.get());
273 if (page_url_.is_valid()) {
275 result += " PAGE_URL=(incognito)" + page_url_.spec();
277 result += " PAGE_URL=" + page_url_.spec();
279 if (!page_title_.empty()) {
280 base::StringValue title(page_title_);
281 result += " PAGE_TITLE=" + Serialize(&title);
283 if (arg_url_.is_valid()) {
285 result += " ARG_URL=(incognito)" + arg_url_.spec();
287 result += " ARG_URL=" + arg_url_.spec();
290 result += " OTHER=" + Serialize(other_.get());
293 result += base::StringPrintf(" COUNT=%d", count_);
297 bool ActionComparator::operator()(
298 const scoped_refptr<Action>& lhs,
299 const scoped_refptr<Action>& rhs) const {
300 if (lhs->time() != rhs->time())
301 return lhs->time() < rhs->time();
302 else if (lhs->action_id() != rhs->action_id())
303 return lhs->action_id() < rhs->action_id();
305 return ActionComparatorExcludingTimeAndActionId()(lhs, rhs);
308 bool ActionComparatorExcludingTimeAndActionId::operator()(
309 const scoped_refptr<Action>& lhs,
310 const scoped_refptr<Action>& rhs) const {
311 if (lhs->extension_id() != rhs->extension_id())
312 return lhs->extension_id() < rhs->extension_id();
313 if (lhs->action_type() != rhs->action_type())
314 return lhs->action_type() < rhs->action_type();
315 if (lhs->api_name() != rhs->api_name())
316 return lhs->api_name() < rhs->api_name();
318 // args might be null; treat a null value as less than all non-null values,
319 // including the empty string.
320 if (!lhs->args() && rhs->args())
322 if (lhs->args() && !rhs->args())
324 if (lhs->args() && rhs->args()) {
325 std::string lhs_args = ActivityLogPolicy::Util::Serialize(lhs->args());
326 std::string rhs_args = ActivityLogPolicy::Util::Serialize(rhs->args());
327 if (lhs_args != rhs_args)
328 return lhs_args < rhs_args;
331 // Compare URLs as strings, and treat the incognito flag as a separate field.
332 if (lhs->page_url().spec() != rhs->page_url().spec())
333 return lhs->page_url().spec() < rhs->page_url().spec();
334 if (lhs->page_incognito() != rhs->page_incognito())
335 return lhs->page_incognito() < rhs->page_incognito();
337 if (lhs->page_title() != rhs->page_title())
338 return lhs->page_title() < rhs->page_title();
340 if (lhs->arg_url().spec() != rhs->arg_url().spec())
341 return lhs->arg_url().spec() < rhs->arg_url().spec();
342 if (lhs->arg_incognito() != rhs->arg_incognito())
343 return lhs->arg_incognito() < rhs->arg_incognito();
345 // other is treated much like the args field.
346 if (!lhs->other() && rhs->other())
348 if (lhs->other() && !rhs->other())
350 if (lhs->other() && rhs->other()) {
351 std::string lhs_other = ActivityLogPolicy::Util::Serialize(lhs->other());
352 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other());
353 if (lhs_other != rhs_other)
354 return lhs_other < rhs_other;
357 // All fields compare as equal if this point is reached.
361 } // namespace extensions