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/format_macros.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/values.h"
15 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
16 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
17 #include "extensions/common/constants.h"
18 #include "extensions/common/dom_action_types.h"
21 namespace constants
= activity_log_constants
;
22 namespace activity_log
= extensions::api::activity_log_private
;
24 namespace extensions
{
28 std::string
Serialize(const base::Value
* value
) {
29 std::string value_as_text
;
31 value_as_text
= "null";
33 JSONStringValueSerializer
serializer(&value_as_text
);
34 serializer
.SerializeAndOmitBinaryValues(*value
);
41 using api::activity_log_private::ExtensionActivity
;
43 Action::Action(const std::string
& extension_id
,
44 const base::Time
& time
,
45 const ActionType action_type
,
46 const std::string
& api_name
,
48 : extension_id_(extension_id
),
50 action_type_(action_type
),
52 page_incognito_(false),
53 arg_incognito_(false),
55 action_id_(action_id
) {}
59 // TODO(mvrable): As an optimization, we might return this directly if the
60 // refcount is one. However, there are likely to be other stray references in
61 // many cases that will prevent this optimization.
62 scoped_refptr
<Action
> Action::Clone() const {
63 scoped_refptr
<Action
> clone(
65 extension_id(), time(), action_type(), api_name(), action_id()));
67 clone
->set_args(make_scoped_ptr(args()->DeepCopy()));
68 clone
->set_page_url(page_url());
69 clone
->set_page_title(page_title());
70 clone
->set_page_incognito(page_incognito());
71 clone
->set_arg_url(arg_url());
72 clone
->set_arg_incognito(arg_incognito());
74 clone
->set_other(make_scoped_ptr(other()->DeepCopy()));
78 void Action::set_args(scoped_ptr
<base::ListValue
> args
) {
79 args_
.reset(args
.release());
82 base::ListValue
* Action::mutable_args() {
84 args_
.reset(new base::ListValue());
89 void Action::set_page_url(const GURL
& page_url
) {
93 void Action::set_arg_url(const GURL
& arg_url
) {
97 void Action::set_other(scoped_ptr
<base::DictionaryValue
> other
) {
98 other_
.reset(other
.release());
101 base::DictionaryValue
* Action::mutable_other() {
103 other_
.reset(new base::DictionaryValue());
108 std::string
Action::SerializePageUrl() const {
109 return (page_incognito() ? constants::kIncognitoUrl
: "") + page_url().spec();
112 void Action::ParsePageUrl(const std::string
& url
) {
113 set_page_incognito(base::StartsWith(url
, constants::kIncognitoUrl
,
114 base::CompareCase::SENSITIVE
));
115 if (page_incognito())
116 set_page_url(GURL(url
.substr(strlen(constants::kIncognitoUrl
))));
118 set_page_url(GURL(url
));
121 std::string
Action::SerializeArgUrl() const {
122 return (arg_incognito() ? constants::kIncognitoUrl
: "") + arg_url().spec();
125 void Action::ParseArgUrl(const std::string
& url
) {
126 set_arg_incognito(base::StartsWith(url
, constants::kIncognitoUrl
,
127 base::CompareCase::SENSITIVE
));
129 set_arg_url(GURL(url
.substr(strlen(constants::kIncognitoUrl
))));
131 set_arg_url(GURL(url
));
134 scoped_ptr
<ExtensionActivity
> Action::ConvertToExtensionActivity() {
135 scoped_ptr
<ExtensionActivity
> result(new ExtensionActivity
);
137 // We do this translation instead of using the same enum because the database
138 // values need to be stable; this allows us to change the extension API
139 // without affecting the database.
140 switch (action_type()) {
141 case ACTION_API_CALL
:
142 result
->activity_type
= activity_log::EXTENSION_ACTIVITY_TYPE_API_CALL
;
144 case ACTION_API_EVENT
:
145 result
->activity_type
= activity_log::EXTENSION_ACTIVITY_TYPE_API_EVENT
;
147 case ACTION_CONTENT_SCRIPT
:
148 result
->activity_type
=
149 activity_log::EXTENSION_ACTIVITY_TYPE_CONTENT_SCRIPT
;
151 case ACTION_DOM_ACCESS
:
152 result
->activity_type
= activity_log::EXTENSION_ACTIVITY_TYPE_DOM_ACCESS
;
154 case ACTION_DOM_EVENT
:
155 result
->activity_type
= activity_log::EXTENSION_ACTIVITY_TYPE_DOM_EVENT
;
157 case ACTION_WEB_REQUEST
:
158 result
->activity_type
= activity_log::EXTENSION_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
= activity_log::EXTENSION_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
=
208 activity_log::EXTENSION_ACTIVITY_DOM_VERB_GETTER
;
210 case DomActionType::SETTER
:
211 other_field
->dom_verb
=
212 activity_log::EXTENSION_ACTIVITY_DOM_VERB_SETTER
;
214 case DomActionType::METHOD
:
215 other_field
->dom_verb
=
216 activity_log::EXTENSION_ACTIVITY_DOM_VERB_METHOD
;
218 case DomActionType::INSERTED
:
219 other_field
->dom_verb
=
220 activity_log::EXTENSION_ACTIVITY_DOM_VERB_INSERTED
;
222 case DomActionType::XHR
:
223 other_field
->dom_verb
= activity_log::EXTENSION_ACTIVITY_DOM_VERB_XHR
;
225 case DomActionType::WEBREQUEST
:
226 other_field
->dom_verb
=
227 activity_log::EXTENSION_ACTIVITY_DOM_VERB_WEBREQUEST
;
229 case DomActionType::MODIFIED
:
230 other_field
->dom_verb
=
231 activity_log::EXTENSION_ACTIVITY_DOM_VERB_MODIFIED
;
234 other_field
->dom_verb
=
235 activity_log::EXTENSION_ACTIVITY_DOM_VERB_NONE
;
238 other_field
->dom_verb
= activity_log::EXTENSION_ACTIVITY_DOM_VERB_NONE
;
240 result
->other
.reset(other_field
.release());
243 return result
.Pass();
246 std::string
Action::PrintForDebug() const {
247 std::string result
= base::StringPrintf("ACTION ID=%" PRId64
, action_id());
248 result
+= " EXTENSION ID=" + extension_id() + " CATEGORY=";
249 switch (action_type_
) {
250 case ACTION_API_CALL
:
251 result
+= "api_call";
253 case ACTION_API_EVENT
:
254 result
+= "api_event_callback";
256 case ACTION_WEB_REQUEST
:
257 result
+= "webrequest";
259 case ACTION_CONTENT_SCRIPT
:
260 result
+= "content_script";
262 case UNUSED_ACTION_API_BLOCKED
:
263 // This is deprecated.
264 result
+= "api_blocked";
266 case ACTION_DOM_EVENT
:
267 result
+= "dom_event";
269 case ACTION_DOM_ACCESS
:
270 result
+= "dom_access";
273 result
+= base::StringPrintf("type%d", static_cast<int>(action_type_
));
276 result
+= " API=" + api_name_
;
278 result
+= " ARGS=" + Serialize(args_
.get());
280 if (page_url_
.is_valid()) {
282 result
+= " PAGE_URL=(incognito)" + page_url_
.spec();
284 result
+= " PAGE_URL=" + page_url_
.spec();
286 if (!page_title_
.empty()) {
287 base::StringValue
title(page_title_
);
288 result
+= " PAGE_TITLE=" + Serialize(&title
);
290 if (arg_url_
.is_valid()) {
292 result
+= " ARG_URL=(incognito)" + arg_url_
.spec();
294 result
+= " ARG_URL=" + arg_url_
.spec();
297 result
+= " OTHER=" + Serialize(other_
.get());
300 result
+= base::StringPrintf(" COUNT=%d", count_
);
304 bool ActionComparator::operator()(
305 const scoped_refptr
<Action
>& lhs
,
306 const scoped_refptr
<Action
>& rhs
) const {
307 if (lhs
->time() != rhs
->time())
308 return lhs
->time() < rhs
->time();
309 else if (lhs
->action_id() != rhs
->action_id())
310 return lhs
->action_id() < rhs
->action_id();
312 return ActionComparatorExcludingTimeAndActionId()(lhs
, rhs
);
315 bool ActionComparatorExcludingTimeAndActionId::operator()(
316 const scoped_refptr
<Action
>& lhs
,
317 const scoped_refptr
<Action
>& rhs
) const {
318 if (lhs
->extension_id() != rhs
->extension_id())
319 return lhs
->extension_id() < rhs
->extension_id();
320 if (lhs
->action_type() != rhs
->action_type())
321 return lhs
->action_type() < rhs
->action_type();
322 if (lhs
->api_name() != rhs
->api_name())
323 return lhs
->api_name() < rhs
->api_name();
325 // args might be null; treat a null value as less than all non-null values,
326 // including the empty string.
327 if (!lhs
->args() && rhs
->args())
329 if (lhs
->args() && !rhs
->args())
331 if (lhs
->args() && rhs
->args()) {
332 std::string lhs_args
= ActivityLogPolicy::Util::Serialize(lhs
->args());
333 std::string rhs_args
= ActivityLogPolicy::Util::Serialize(rhs
->args());
334 if (lhs_args
!= rhs_args
)
335 return lhs_args
< rhs_args
;
338 // Compare URLs as strings, and treat the incognito flag as a separate field.
339 if (lhs
->page_url().spec() != rhs
->page_url().spec())
340 return lhs
->page_url().spec() < rhs
->page_url().spec();
341 if (lhs
->page_incognito() != rhs
->page_incognito())
342 return lhs
->page_incognito() < rhs
->page_incognito();
344 if (lhs
->page_title() != rhs
->page_title())
345 return lhs
->page_title() < rhs
->page_title();
347 if (lhs
->arg_url().spec() != rhs
->arg_url().spec())
348 return lhs
->arg_url().spec() < rhs
->arg_url().spec();
349 if (lhs
->arg_incognito() != rhs
->arg_incognito())
350 return lhs
->arg_incognito() < rhs
->arg_incognito();
352 // other is treated much like the args field.
353 if (!lhs
->other() && rhs
->other())
355 if (lhs
->other() && !rhs
->other())
357 if (lhs
->other() && rhs
->other()) {
358 std::string lhs_other
= ActivityLogPolicy::Util::Serialize(lhs
->other());
359 std::string rhs_other
= ActivityLogPolicy::Util::Serialize(rhs
->other());
360 if (lhs_other
!= rhs_other
)
361 return lhs_other
< rhs_other
;
364 // All fields compare as equal if this point is reached.
368 } // namespace extensions