Give names to all utility processes.
[chromium-blink-merge.git] / chrome / browser / extensions / activity_log / activity_actions.cc
blob63f9774ed42e9610341deb24ed705be0a76aad62
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"
7 #include <algorithm> // for std::find.
8 #include <string>
10 #include "base/command_line.h"
11 #include "base/format_macros.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/memory/singleton.h"
16 #include "base/metrics/histogram.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/values.h"
21 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
22 #include "chrome/browser/extensions/activity_log/ad_network_database.h"
23 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "components/rappor/rappor_service.h"
27 #include "content/public/browser/web_contents.h"
28 #include "extensions/common/constants.h"
29 #include "extensions/common/dom_action_types.h"
30 #include "sql/statement.h"
31 #include "url/gurl.h"
33 namespace constants = activity_log_constants;
34 namespace activity_log = extensions::api::activity_log_private;
36 namespace extensions {
38 namespace {
40 // The "Extensions.PossibleAdInjection2" metric uses different Rappor
41 // parameters than the original metric.
42 const char kExtensionAdInjectionRapporMetricName[] =
43 "Extensions.PossibleAdInjection2";
45 const char kBlinkSetAttributeEvent[] = "blinkSetAttribute";
46 const char kBlinkAddElementEvent[] = "blinkAddElement";
48 const char kIframe[] = "iframe";
49 const char kAnchor[] = "a";
50 const char kScript[] = "script";
52 const char kSrc[] = "src";
53 const char kHref[] = "href";
55 std::string Serialize(const base::Value* value) {
56 std::string value_as_text;
57 if (!value) {
58 value_as_text = "null";
59 } else {
60 JSONStringValueSerializer serializer(&value_as_text);
61 serializer.SerializeAndOmitBinaryValues(*value);
63 return value_as_text;
66 } // namespace
68 using api::activity_log_private::ExtensionActivity;
70 Action::Action(const std::string& extension_id,
71 const base::Time& time,
72 const ActionType action_type,
73 const std::string& api_name,
74 int64 action_id)
75 : extension_id_(extension_id),
76 time_(time),
77 action_type_(action_type),
78 api_name_(api_name),
79 page_incognito_(false),
80 arg_incognito_(false),
81 count_(0),
82 action_id_(action_id) {}
84 Action::~Action() {}
86 // TODO(mvrable): As an optimization, we might return this directly if the
87 // refcount is one. However, there are likely to be other stray references in
88 // many cases that will prevent this optimization.
89 scoped_refptr<Action> Action::Clone() const {
90 scoped_refptr<Action> clone(
91 new Action(
92 extension_id(), time(), action_type(), api_name(), action_id()));
93 if (args())
94 clone->set_args(make_scoped_ptr(args()->DeepCopy()));
95 clone->set_page_url(page_url());
96 clone->set_page_title(page_title());
97 clone->set_page_incognito(page_incognito());
98 clone->set_arg_url(arg_url());
99 clone->set_arg_incognito(arg_incognito());
100 if (other())
101 clone->set_other(make_scoped_ptr(other()->DeepCopy()));
102 return clone;
105 Action::InjectionType Action::DidInjectAd(
106 rappor::RapporService* rappor_service) const {
107 MaybeUploadUrl(rappor_service);
109 // We should always have an AdNetworkDatabase, but, on the offchance we don't,
110 // don't crash in a release build.
111 if (!AdNetworkDatabase::Get()) {
112 NOTREACHED();
113 return NO_AD_INJECTION;
116 AdType ad_type = AD_TYPE_NONE;
117 InjectionType injection_type = NO_AD_INJECTION;
119 if (api_name_ == kBlinkSetAttributeEvent) {
120 std::string element_name;
121 std::string attr_name;
122 if (args_.get()) {
123 args_->GetString(0u, &element_name);
124 args_->GetString(1u, &attr_name);
126 if (attr_name == kSrc) {
127 if (element_name == kIframe)
128 ad_type = AD_TYPE_IFRAME;
129 else if (element_name == kScript)
130 ad_type = AD_TYPE_SCRIPT;
131 } else if (element_name == kAnchor && attr_name == kHref) {
132 ad_type = AD_TYPE_ANCHOR;
135 if (ad_type != AD_TYPE_NONE)
136 injection_type = CheckAttrModification();
137 } else if (api_name_ == kBlinkAddElementEvent) {
138 std::string element_name;
139 if (args_.get())
140 args_->GetString(0u, &element_name);
141 if (element_name == kIframe)
142 ad_type = AD_TYPE_IFRAME;
143 else if (element_name == kAnchor)
144 ad_type = AD_TYPE_ANCHOR;
145 else if (element_name == kScript)
146 ad_type = AD_TYPE_SCRIPT;
148 if (ad_type != AD_TYPE_NONE)
149 injection_type = CheckElementAddition();
152 if (injection_type != NO_AD_INJECTION) {
153 UMA_HISTOGRAM_ENUMERATION(
154 "Extensions.AdInjection.AdType", ad_type, Action::NUM_AD_TYPES);
157 return injection_type;
160 void Action::set_args(scoped_ptr<base::ListValue> args) {
161 args_.reset(args.release());
164 base::ListValue* Action::mutable_args() {
165 if (!args_.get()) {
166 args_.reset(new base::ListValue());
168 return args_.get();
171 void Action::set_page_url(const GURL& page_url) {
172 page_url_ = page_url;
175 void Action::set_arg_url(const GURL& arg_url) {
176 arg_url_ = arg_url;
179 void Action::set_other(scoped_ptr<base::DictionaryValue> other) {
180 other_.reset(other.release());
183 base::DictionaryValue* Action::mutable_other() {
184 if (!other_.get()) {
185 other_.reset(new base::DictionaryValue());
187 return other_.get();
190 std::string Action::SerializePageUrl() const {
191 return (page_incognito() ? constants::kIncognitoUrl : "") + page_url().spec();
194 void Action::ParsePageUrl(const std::string& url) {
195 set_page_incognito(StartsWithASCII(url, constants::kIncognitoUrl, true));
196 if (page_incognito())
197 set_page_url(GURL(url.substr(strlen(constants::kIncognitoUrl))));
198 else
199 set_page_url(GURL(url));
202 std::string Action::SerializeArgUrl() const {
203 return (arg_incognito() ? constants::kIncognitoUrl : "") + arg_url().spec();
206 void Action::ParseArgUrl(const std::string& url) {
207 set_arg_incognito(StartsWithASCII(url, constants::kIncognitoUrl, true));
208 if (arg_incognito())
209 set_arg_url(GURL(url.substr(strlen(constants::kIncognitoUrl))));
210 else
211 set_arg_url(GURL(url));
214 scoped_ptr<ExtensionActivity> Action::ConvertToExtensionActivity() {
215 scoped_ptr<ExtensionActivity> result(new ExtensionActivity);
217 // We do this translation instead of using the same enum because the database
218 // values need to be stable; this allows us to change the extension API
219 // without affecting the database.
220 switch (action_type()) {
221 case ACTION_API_CALL:
222 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_API_CALL;
223 break;
224 case ACTION_API_EVENT:
225 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_API_EVENT;
226 break;
227 case ACTION_CONTENT_SCRIPT:
228 result->activity_type =
229 activity_log::EXTENSION_ACTIVITY_TYPE_CONTENT_SCRIPT;
230 break;
231 case ACTION_DOM_ACCESS:
232 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_DOM_ACCESS;
233 break;
234 case ACTION_DOM_EVENT:
235 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_DOM_EVENT;
236 break;
237 case ACTION_WEB_REQUEST:
238 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_WEB_REQUEST;
239 break;
240 case UNUSED_ACTION_API_BLOCKED:
241 case ACTION_ANY:
242 default:
243 // This shouldn't be reached, but some people might have old or otherwise
244 // weird db entries. Treat it like an API call if that happens.
245 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_API_CALL;
246 break;
249 result->extension_id.reset(new std::string(extension_id()));
250 result->time.reset(new double(time().ToJsTime()));
251 result->count.reset(new double(count()));
252 result->api_call.reset(new std::string(api_name()));
253 result->args.reset(new std::string(Serialize(args())));
254 if (action_id() != -1)
255 result->activity_id.reset(
256 new std::string(base::StringPrintf("%" PRId64, action_id())));
257 if (page_url().is_valid()) {
258 if (!page_title().empty())
259 result->page_title.reset(new std::string(page_title()));
260 result->page_url.reset(new std::string(SerializePageUrl()));
262 if (arg_url().is_valid())
263 result->arg_url.reset(new std::string(SerializeArgUrl()));
265 if (other()) {
266 scoped_ptr<ExtensionActivity::Other> other_field(
267 new ExtensionActivity::Other);
268 bool prerender;
269 if (other()->GetBooleanWithoutPathExpansion(constants::kActionPrerender,
270 &prerender)) {
271 other_field->prerender.reset(new bool(prerender));
273 const base::DictionaryValue* web_request;
274 if (other()->GetDictionaryWithoutPathExpansion(constants::kActionWebRequest,
275 &web_request)) {
276 other_field->web_request.reset(new std::string(
277 ActivityLogPolicy::Util::Serialize(web_request)));
279 std::string extra;
280 if (other()->GetStringWithoutPathExpansion(constants::kActionExtra, &extra))
281 other_field->extra.reset(new std::string(extra));
282 int dom_verb;
283 if (other()->GetIntegerWithoutPathExpansion(constants::kActionDomVerb,
284 &dom_verb)) {
285 switch (static_cast<DomActionType::Type>(dom_verb)) {
286 case DomActionType::GETTER:
287 other_field->dom_verb =
288 activity_log::EXTENSION_ACTIVITY_DOM_VERB_GETTER;
289 break;
290 case DomActionType::SETTER:
291 other_field->dom_verb =
292 activity_log::EXTENSION_ACTIVITY_DOM_VERB_SETTER;
293 break;
294 case DomActionType::METHOD:
295 other_field->dom_verb =
296 activity_log::EXTENSION_ACTIVITY_DOM_VERB_METHOD;
297 break;
298 case DomActionType::INSERTED:
299 other_field->dom_verb =
300 activity_log::EXTENSION_ACTIVITY_DOM_VERB_INSERTED;
301 break;
302 case DomActionType::XHR:
303 other_field->dom_verb = activity_log::EXTENSION_ACTIVITY_DOM_VERB_XHR;
304 break;
305 case DomActionType::WEBREQUEST:
306 other_field->dom_verb =
307 activity_log::EXTENSION_ACTIVITY_DOM_VERB_WEBREQUEST;
308 break;
309 case DomActionType::MODIFIED:
310 other_field->dom_verb =
311 activity_log::EXTENSION_ACTIVITY_DOM_VERB_MODIFIED;
312 break;
313 default:
314 other_field->dom_verb =
315 activity_log::EXTENSION_ACTIVITY_DOM_VERB_NONE;
317 } else {
318 other_field->dom_verb = activity_log::EXTENSION_ACTIVITY_DOM_VERB_NONE;
320 result->other.reset(other_field.release());
323 return result.Pass();
326 std::string Action::PrintForDebug() const {
327 std::string result = base::StringPrintf("ACTION ID=%" PRId64, action_id());
328 result += " EXTENSION ID=" + extension_id() + " CATEGORY=";
329 switch (action_type_) {
330 case ACTION_API_CALL:
331 result += "api_call";
332 break;
333 case ACTION_API_EVENT:
334 result += "api_event_callback";
335 break;
336 case ACTION_WEB_REQUEST:
337 result += "webrequest";
338 break;
339 case ACTION_CONTENT_SCRIPT:
340 result += "content_script";
341 break;
342 case UNUSED_ACTION_API_BLOCKED:
343 // This is deprecated.
344 result += "api_blocked";
345 break;
346 case ACTION_DOM_EVENT:
347 result += "dom_event";
348 break;
349 case ACTION_DOM_ACCESS:
350 result += "dom_access";
351 break;
352 default:
353 result += base::StringPrintf("type%d", static_cast<int>(action_type_));
356 result += " API=" + api_name_;
357 if (args_.get()) {
358 result += " ARGS=" + Serialize(args_.get());
360 if (page_url_.is_valid()) {
361 if (page_incognito_)
362 result += " PAGE_URL=(incognito)" + page_url_.spec();
363 else
364 result += " PAGE_URL=" + page_url_.spec();
366 if (!page_title_.empty()) {
367 base::StringValue title(page_title_);
368 result += " PAGE_TITLE=" + Serialize(&title);
370 if (arg_url_.is_valid()) {
371 if (arg_incognito_)
372 result += " ARG_URL=(incognito)" + arg_url_.spec();
373 else
374 result += " ARG_URL=" + arg_url_.spec();
376 if (other_.get()) {
377 result += " OTHER=" + Serialize(other_.get());
380 result += base::StringPrintf(" COUNT=%d", count_);
381 return result;
384 bool Action::UrlCouldBeAd(const GURL& url) const {
385 // Ads can only be valid urls that don't match the page's host (linking to the
386 // current page should be considered valid use), and aren't local to the
387 // extension.
388 return url.is_valid() &&
389 !url.is_empty() &&
390 url.host() != page_url_.host() &&
391 !url.SchemeIs(kExtensionScheme);
394 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const {
395 // Don't bother recording if the url is innocuous (or no |rappor_service|).
396 if (!rappor_service)
397 return;
399 GURL url;
401 if (api_name_ == kBlinkSetAttributeEvent) {
402 std::string element_name;
403 std::string attr_name;
404 std::string url_string;
405 if (args_.get()) {
406 args_->GetString(0u, &element_name);
407 args_->GetString(1u, &attr_name);
409 if (element_name == kIframe && attr_name == kSrc) {
410 args_->GetString(3u, &url_string);
411 url = GURL(url_string);
412 } else if (element_name == kAnchor && attr_name == kHref) {
413 args_->GetString(3u, &url_string);
414 url = GURL(url_string);
416 } else if (api_name_ == kBlinkAddElementEvent) {
417 std::string element_name;
418 std::string url_string;
419 if (args_.get())
420 args_->GetString(0u, &element_name);
421 if (element_name == kIframe) {
422 args_->GetString(1u, &url_string);
423 url = GURL(url_string);
424 } else if (element_name == kAnchor) {
425 args_->GetString(1u, &url_string);
426 url = GURL(url_string);
430 if (!UrlCouldBeAd(url))
431 return;
433 // Record the URL - an ad *may* have been injected.
434 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName,
435 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
436 url.host());
439 Action::InjectionType Action::CheckAttrModification() const {
440 if (api_name_ != kBlinkSetAttributeEvent)
441 return NO_AD_INJECTION;
443 const AdNetworkDatabase* database = AdNetworkDatabase::Get();
445 GURL prev_url;
446 std::string prev_url_string;
447 if (args_.get() && args_->GetString(2u, &prev_url_string))
448 prev_url = GURL(prev_url_string);
450 GURL new_url;
451 std::string new_url_string;
452 if (args_.get() && args_->GetString(3u, &new_url_string))
453 new_url = GURL(new_url_string);
455 bool new_url_could_be_ad = UrlCouldBeAd(new_url);
456 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty();
458 bool injected_ad = new_url_could_be_ad && database->IsAdNetwork(new_url);
459 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url);
461 if (injected_ad && replaced_ad)
462 return INJECTION_REPLACED_AD;
463 if (injected_ad)
464 return INJECTION_NEW_AD;
465 if (replaced_ad)
466 return INJECTION_REMOVED_AD;
468 // If the extension modified the URL with an external, valid URL then there's
469 // a good chance it's ad injection. Log it as a likely one, which also helps
470 // us determine the effectiveness of our IsAdNetwork() recognition.
471 if (new_url_could_be_ad) {
472 if (prev_url_valid)
473 return INJECTION_LIKELY_REPLACED_AD;
474 return INJECTION_LIKELY_NEW_AD;
477 return NO_AD_INJECTION;
480 Action::InjectionType Action::CheckElementAddition() const {
481 DCHECK_EQ(kBlinkAddElementEvent, api_name_);
483 GURL url;
484 std::string url_string;
485 if (args_.get() && args_->GetString(1u, &url_string))
486 url = GURL(url_string);
488 if (UrlCouldBeAd(url)) {
489 if (AdNetworkDatabase::Get()->IsAdNetwork(url))
490 return INJECTION_NEW_AD;
491 // If the extension injected an URL which is not local to itself or the
492 // page, there is a good chance it could be a new ad, and our database
493 // missed it.
494 return INJECTION_LIKELY_NEW_AD;
496 return NO_AD_INJECTION;
499 bool ActionComparator::operator()(
500 const scoped_refptr<Action>& lhs,
501 const scoped_refptr<Action>& rhs) const {
502 if (lhs->time() != rhs->time())
503 return lhs->time() < rhs->time();
504 else if (lhs->action_id() != rhs->action_id())
505 return lhs->action_id() < rhs->action_id();
506 else
507 return ActionComparatorExcludingTimeAndActionId()(lhs, rhs);
510 bool ActionComparatorExcludingTimeAndActionId::operator()(
511 const scoped_refptr<Action>& lhs,
512 const scoped_refptr<Action>& rhs) const {
513 if (lhs->extension_id() != rhs->extension_id())
514 return lhs->extension_id() < rhs->extension_id();
515 if (lhs->action_type() != rhs->action_type())
516 return lhs->action_type() < rhs->action_type();
517 if (lhs->api_name() != rhs->api_name())
518 return lhs->api_name() < rhs->api_name();
520 // args might be null; treat a null value as less than all non-null values,
521 // including the empty string.
522 if (!lhs->args() && rhs->args())
523 return true;
524 if (lhs->args() && !rhs->args())
525 return false;
526 if (lhs->args() && rhs->args()) {
527 std::string lhs_args = ActivityLogPolicy::Util::Serialize(lhs->args());
528 std::string rhs_args = ActivityLogPolicy::Util::Serialize(rhs->args());
529 if (lhs_args != rhs_args)
530 return lhs_args < rhs_args;
533 // Compare URLs as strings, and treat the incognito flag as a separate field.
534 if (lhs->page_url().spec() != rhs->page_url().spec())
535 return lhs->page_url().spec() < rhs->page_url().spec();
536 if (lhs->page_incognito() != rhs->page_incognito())
537 return lhs->page_incognito() < rhs->page_incognito();
539 if (lhs->page_title() != rhs->page_title())
540 return lhs->page_title() < rhs->page_title();
542 if (lhs->arg_url().spec() != rhs->arg_url().spec())
543 return lhs->arg_url().spec() < rhs->arg_url().spec();
544 if (lhs->arg_incognito() != rhs->arg_incognito())
545 return lhs->arg_incognito() < rhs->arg_incognito();
547 // other is treated much like the args field.
548 if (!lhs->other() && rhs->other())
549 return true;
550 if (lhs->other() && !rhs->other())
551 return false;
552 if (lhs->other() && rhs->other()) {
553 std::string lhs_other = ActivityLogPolicy::Util::Serialize(lhs->other());
554 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other());
555 if (lhs_other != rhs_other)
556 return lhs_other < rhs_other;
559 // All fields compare as equal if this point is reached.
560 return false;
563 } // namespace extensions