[sql] Remove _HAS_EXCEPTIONS=0 from build info.
[chromium-blink-merge.git] / chrome / browser / extensions / activity_log / activity_actions.cc
blob2f83cbb48ebf7ff0e73c1ec6e19919ee5b5e34c7
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 "url/gurl.h"
32 namespace constants = activity_log_constants;
33 namespace activity_log = extensions::api::activity_log_private;
35 namespace extensions {
37 namespace {
39 // The "Extensions.PossibleAdInjection2" metric uses different Rappor
40 // parameters than the original metric.
41 const char kExtensionAdInjectionRapporMetricName[] =
42 "Extensions.PossibleAdInjection2";
44 const char kBlinkSetAttributeEvent[] = "blinkSetAttribute";
45 const char kBlinkAddElementEvent[] = "blinkAddElement";
47 const char kIframe[] = "iframe";
48 const char kAnchor[] = "a";
49 const char kScript[] = "script";
51 const char kSrc[] = "src";
52 const char kHref[] = "href";
54 std::string Serialize(const base::Value* value) {
55 std::string value_as_text;
56 if (!value) {
57 value_as_text = "null";
58 } else {
59 JSONStringValueSerializer serializer(&value_as_text);
60 serializer.SerializeAndOmitBinaryValues(*value);
62 return value_as_text;
65 } // namespace
67 using api::activity_log_private::ExtensionActivity;
69 Action::Action(const std::string& extension_id,
70 const base::Time& time,
71 const ActionType action_type,
72 const std::string& api_name,
73 int64 action_id)
74 : extension_id_(extension_id),
75 time_(time),
76 action_type_(action_type),
77 api_name_(api_name),
78 page_incognito_(false),
79 arg_incognito_(false),
80 count_(0),
81 action_id_(action_id) {}
83 Action::~Action() {}
85 // TODO(mvrable): As an optimization, we might return this directly if the
86 // refcount is one. However, there are likely to be other stray references in
87 // many cases that will prevent this optimization.
88 scoped_refptr<Action> Action::Clone() const {
89 scoped_refptr<Action> clone(
90 new Action(
91 extension_id(), time(), action_type(), api_name(), action_id()));
92 if (args())
93 clone->set_args(make_scoped_ptr(args()->DeepCopy()));
94 clone->set_page_url(page_url());
95 clone->set_page_title(page_title());
96 clone->set_page_incognito(page_incognito());
97 clone->set_arg_url(arg_url());
98 clone->set_arg_incognito(arg_incognito());
99 if (other())
100 clone->set_other(make_scoped_ptr(other()->DeepCopy()));
101 return clone;
104 Action::InjectionType Action::DidInjectAd(
105 rappor::RapporService* rappor_service) const {
106 MaybeUploadUrl(rappor_service);
108 // We should always have an AdNetworkDatabase, but, on the offchance we don't,
109 // don't crash in a release build.
110 if (!AdNetworkDatabase::Get()) {
111 NOTREACHED();
112 return NO_AD_INJECTION;
115 AdType ad_type = AD_TYPE_NONE;
116 InjectionType injection_type = NO_AD_INJECTION;
118 if (api_name_ == kBlinkSetAttributeEvent) {
119 std::string element_name;
120 std::string attr_name;
121 if (args_.get()) {
122 args_->GetString(0u, &element_name);
123 args_->GetString(1u, &attr_name);
125 if (attr_name == kSrc) {
126 if (element_name == kIframe)
127 ad_type = AD_TYPE_IFRAME;
128 else if (element_name == kScript)
129 ad_type = AD_TYPE_SCRIPT;
130 } else if (element_name == kAnchor && attr_name == kHref) {
131 ad_type = AD_TYPE_ANCHOR;
134 if (ad_type != AD_TYPE_NONE)
135 injection_type = CheckAttrModification();
136 } else if (api_name_ == kBlinkAddElementEvent) {
137 std::string element_name;
138 if (args_.get())
139 args_->GetString(0u, &element_name);
140 if (element_name == kIframe)
141 ad_type = AD_TYPE_IFRAME;
142 else if (element_name == kAnchor)
143 ad_type = AD_TYPE_ANCHOR;
144 else if (element_name == kScript)
145 ad_type = AD_TYPE_SCRIPT;
147 if (ad_type != AD_TYPE_NONE)
148 injection_type = CheckElementAddition();
151 if (injection_type != NO_AD_INJECTION) {
152 UMA_HISTOGRAM_ENUMERATION(
153 "Extensions.AdInjection.AdType", ad_type, Action::NUM_AD_TYPES);
156 return injection_type;
159 void Action::set_args(scoped_ptr<base::ListValue> args) {
160 args_.reset(args.release());
163 base::ListValue* Action::mutable_args() {
164 if (!args_.get()) {
165 args_.reset(new base::ListValue());
167 return args_.get();
170 void Action::set_page_url(const GURL& page_url) {
171 page_url_ = page_url;
174 void Action::set_arg_url(const GURL& arg_url) {
175 arg_url_ = arg_url;
178 void Action::set_other(scoped_ptr<base::DictionaryValue> other) {
179 other_.reset(other.release());
182 base::DictionaryValue* Action::mutable_other() {
183 if (!other_.get()) {
184 other_.reset(new base::DictionaryValue());
186 return other_.get();
189 std::string Action::SerializePageUrl() const {
190 return (page_incognito() ? constants::kIncognitoUrl : "") + page_url().spec();
193 void Action::ParsePageUrl(const std::string& url) {
194 set_page_incognito(base::StartsWith(url, constants::kIncognitoUrl,
195 base::CompareCase::SENSITIVE));
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(base::StartsWith(url, constants::kIncognitoUrl,
208 base::CompareCase::SENSITIVE));
209 if (arg_incognito())
210 set_arg_url(GURL(url.substr(strlen(constants::kIncognitoUrl))));
211 else
212 set_arg_url(GURL(url));
215 scoped_ptr<ExtensionActivity> Action::ConvertToExtensionActivity() {
216 scoped_ptr<ExtensionActivity> result(new ExtensionActivity);
218 // We do this translation instead of using the same enum because the database
219 // values need to be stable; this allows us to change the extension API
220 // without affecting the database.
221 switch (action_type()) {
222 case ACTION_API_CALL:
223 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_API_CALL;
224 break;
225 case ACTION_API_EVENT:
226 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_API_EVENT;
227 break;
228 case ACTION_CONTENT_SCRIPT:
229 result->activity_type =
230 activity_log::EXTENSION_ACTIVITY_TYPE_CONTENT_SCRIPT;
231 break;
232 case ACTION_DOM_ACCESS:
233 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_DOM_ACCESS;
234 break;
235 case ACTION_DOM_EVENT:
236 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_DOM_EVENT;
237 break;
238 case ACTION_WEB_REQUEST:
239 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_WEB_REQUEST;
240 break;
241 case UNUSED_ACTION_API_BLOCKED:
242 case ACTION_ANY:
243 default:
244 // This shouldn't be reached, but some people might have old or otherwise
245 // weird db entries. Treat it like an API call if that happens.
246 result->activity_type = activity_log::EXTENSION_ACTIVITY_TYPE_API_CALL;
247 break;
250 result->extension_id.reset(new std::string(extension_id()));
251 result->time.reset(new double(time().ToJsTime()));
252 result->count.reset(new double(count()));
253 result->api_call.reset(new std::string(api_name()));
254 result->args.reset(new std::string(Serialize(args())));
255 if (action_id() != -1)
256 result->activity_id.reset(
257 new std::string(base::StringPrintf("%" PRId64, action_id())));
258 if (page_url().is_valid()) {
259 if (!page_title().empty())
260 result->page_title.reset(new std::string(page_title()));
261 result->page_url.reset(new std::string(SerializePageUrl()));
263 if (arg_url().is_valid())
264 result->arg_url.reset(new std::string(SerializeArgUrl()));
266 if (other()) {
267 scoped_ptr<ExtensionActivity::Other> other_field(
268 new ExtensionActivity::Other);
269 bool prerender;
270 if (other()->GetBooleanWithoutPathExpansion(constants::kActionPrerender,
271 &prerender)) {
272 other_field->prerender.reset(new bool(prerender));
274 const base::DictionaryValue* web_request;
275 if (other()->GetDictionaryWithoutPathExpansion(constants::kActionWebRequest,
276 &web_request)) {
277 other_field->web_request.reset(new std::string(
278 ActivityLogPolicy::Util::Serialize(web_request)));
280 std::string extra;
281 if (other()->GetStringWithoutPathExpansion(constants::kActionExtra, &extra))
282 other_field->extra.reset(new std::string(extra));
283 int dom_verb;
284 if (other()->GetIntegerWithoutPathExpansion(constants::kActionDomVerb,
285 &dom_verb)) {
286 switch (static_cast<DomActionType::Type>(dom_verb)) {
287 case DomActionType::GETTER:
288 other_field->dom_verb =
289 activity_log::EXTENSION_ACTIVITY_DOM_VERB_GETTER;
290 break;
291 case DomActionType::SETTER:
292 other_field->dom_verb =
293 activity_log::EXTENSION_ACTIVITY_DOM_VERB_SETTER;
294 break;
295 case DomActionType::METHOD:
296 other_field->dom_verb =
297 activity_log::EXTENSION_ACTIVITY_DOM_VERB_METHOD;
298 break;
299 case DomActionType::INSERTED:
300 other_field->dom_verb =
301 activity_log::EXTENSION_ACTIVITY_DOM_VERB_INSERTED;
302 break;
303 case DomActionType::XHR:
304 other_field->dom_verb = activity_log::EXTENSION_ACTIVITY_DOM_VERB_XHR;
305 break;
306 case DomActionType::WEBREQUEST:
307 other_field->dom_verb =
308 activity_log::EXTENSION_ACTIVITY_DOM_VERB_WEBREQUEST;
309 break;
310 case DomActionType::MODIFIED:
311 other_field->dom_verb =
312 activity_log::EXTENSION_ACTIVITY_DOM_VERB_MODIFIED;
313 break;
314 default:
315 other_field->dom_verb =
316 activity_log::EXTENSION_ACTIVITY_DOM_VERB_NONE;
318 } else {
319 other_field->dom_verb = activity_log::EXTENSION_ACTIVITY_DOM_VERB_NONE;
321 result->other.reset(other_field.release());
324 return result.Pass();
327 std::string Action::PrintForDebug() const {
328 std::string result = base::StringPrintf("ACTION ID=%" PRId64, action_id());
329 result += " EXTENSION ID=" + extension_id() + " CATEGORY=";
330 switch (action_type_) {
331 case ACTION_API_CALL:
332 result += "api_call";
333 break;
334 case ACTION_API_EVENT:
335 result += "api_event_callback";
336 break;
337 case ACTION_WEB_REQUEST:
338 result += "webrequest";
339 break;
340 case ACTION_CONTENT_SCRIPT:
341 result += "content_script";
342 break;
343 case UNUSED_ACTION_API_BLOCKED:
344 // This is deprecated.
345 result += "api_blocked";
346 break;
347 case ACTION_DOM_EVENT:
348 result += "dom_event";
349 break;
350 case ACTION_DOM_ACCESS:
351 result += "dom_access";
352 break;
353 default:
354 result += base::StringPrintf("type%d", static_cast<int>(action_type_));
357 result += " API=" + api_name_;
358 if (args_.get()) {
359 result += " ARGS=" + Serialize(args_.get());
361 if (page_url_.is_valid()) {
362 if (page_incognito_)
363 result += " PAGE_URL=(incognito)" + page_url_.spec();
364 else
365 result += " PAGE_URL=" + page_url_.spec();
367 if (!page_title_.empty()) {
368 base::StringValue title(page_title_);
369 result += " PAGE_TITLE=" + Serialize(&title);
371 if (arg_url_.is_valid()) {
372 if (arg_incognito_)
373 result += " ARG_URL=(incognito)" + arg_url_.spec();
374 else
375 result += " ARG_URL=" + arg_url_.spec();
377 if (other_.get()) {
378 result += " OTHER=" + Serialize(other_.get());
381 result += base::StringPrintf(" COUNT=%d", count_);
382 return result;
385 bool Action::UrlCouldBeAd(const GURL& url) const {
386 // Ads can only be valid urls that don't match the page's host (linking to the
387 // current page should be considered valid use), and aren't local to the
388 // extension.
389 return url.is_valid() &&
390 !url.is_empty() &&
391 url.host() != page_url_.host() &&
392 !url.SchemeIs(kExtensionScheme);
395 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const {
396 // Don't bother recording if the url is innocuous (or no |rappor_service|).
397 if (!rappor_service)
398 return;
400 GURL url;
402 if (api_name_ == kBlinkSetAttributeEvent) {
403 std::string element_name;
404 std::string attr_name;
405 std::string url_string;
406 if (args_.get()) {
407 args_->GetString(0u, &element_name);
408 args_->GetString(1u, &attr_name);
410 if (element_name == kIframe && attr_name == kSrc) {
411 args_->GetString(3u, &url_string);
412 url = GURL(url_string);
413 } else if (element_name == kAnchor && attr_name == kHref) {
414 args_->GetString(3u, &url_string);
415 url = GURL(url_string);
417 } else if (api_name_ == kBlinkAddElementEvent) {
418 std::string element_name;
419 std::string url_string;
420 if (args_.get())
421 args_->GetString(0u, &element_name);
422 if (element_name == kIframe) {
423 args_->GetString(1u, &url_string);
424 url = GURL(url_string);
425 } else if (element_name == kAnchor) {
426 args_->GetString(1u, &url_string);
427 url = GURL(url_string);
431 if (!UrlCouldBeAd(url))
432 return;
434 // Record the URL - an ad *may* have been injected.
435 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName,
436 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
437 url.host());
440 Action::InjectionType Action::CheckAttrModification() const {
441 if (api_name_ != kBlinkSetAttributeEvent)
442 return NO_AD_INJECTION;
444 const AdNetworkDatabase* database = AdNetworkDatabase::Get();
446 GURL prev_url;
447 std::string prev_url_string;
448 if (args_.get() && args_->GetString(2u, &prev_url_string))
449 prev_url = GURL(prev_url_string);
451 GURL new_url;
452 std::string new_url_string;
453 if (args_.get() && args_->GetString(3u, &new_url_string))
454 new_url = GURL(new_url_string);
456 bool new_url_could_be_ad = UrlCouldBeAd(new_url);
457 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty();
459 bool injected_ad = new_url_could_be_ad && database->IsAdNetwork(new_url);
460 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url);
462 if (injected_ad && replaced_ad)
463 return INJECTION_REPLACED_AD;
464 if (injected_ad)
465 return INJECTION_NEW_AD;
466 if (replaced_ad)
467 return INJECTION_REMOVED_AD;
469 // If the extension modified the URL with an external, valid URL then there's
470 // a good chance it's ad injection. Log it as a likely one, which also helps
471 // us determine the effectiveness of our IsAdNetwork() recognition.
472 if (new_url_could_be_ad) {
473 if (prev_url_valid)
474 return INJECTION_LIKELY_REPLACED_AD;
475 return INJECTION_LIKELY_NEW_AD;
478 return NO_AD_INJECTION;
481 Action::InjectionType Action::CheckElementAddition() const {
482 DCHECK_EQ(kBlinkAddElementEvent, api_name_);
484 GURL url;
485 std::string url_string;
486 if (args_.get() && args_->GetString(1u, &url_string))
487 url = GURL(url_string);
489 if (UrlCouldBeAd(url)) {
490 if (AdNetworkDatabase::Get()->IsAdNetwork(url))
491 return INJECTION_NEW_AD;
492 // If the extension injected an URL which is not local to itself or the
493 // page, there is a good chance it could be a new ad, and our database
494 // missed it.
495 return INJECTION_LIKELY_NEW_AD;
497 return NO_AD_INJECTION;
500 bool ActionComparator::operator()(
501 const scoped_refptr<Action>& lhs,
502 const scoped_refptr<Action>& rhs) const {
503 if (lhs->time() != rhs->time())
504 return lhs->time() < rhs->time();
505 else if (lhs->action_id() != rhs->action_id())
506 return lhs->action_id() < rhs->action_id();
507 else
508 return ActionComparatorExcludingTimeAndActionId()(lhs, rhs);
511 bool ActionComparatorExcludingTimeAndActionId::operator()(
512 const scoped_refptr<Action>& lhs,
513 const scoped_refptr<Action>& rhs) const {
514 if (lhs->extension_id() != rhs->extension_id())
515 return lhs->extension_id() < rhs->extension_id();
516 if (lhs->action_type() != rhs->action_type())
517 return lhs->action_type() < rhs->action_type();
518 if (lhs->api_name() != rhs->api_name())
519 return lhs->api_name() < rhs->api_name();
521 // args might be null; treat a null value as less than all non-null values,
522 // including the empty string.
523 if (!lhs->args() && rhs->args())
524 return true;
525 if (lhs->args() && !rhs->args())
526 return false;
527 if (lhs->args() && rhs->args()) {
528 std::string lhs_args = ActivityLogPolicy::Util::Serialize(lhs->args());
529 std::string rhs_args = ActivityLogPolicy::Util::Serialize(rhs->args());
530 if (lhs_args != rhs_args)
531 return lhs_args < rhs_args;
534 // Compare URLs as strings, and treat the incognito flag as a separate field.
535 if (lhs->page_url().spec() != rhs->page_url().spec())
536 return lhs->page_url().spec() < rhs->page_url().spec();
537 if (lhs->page_incognito() != rhs->page_incognito())
538 return lhs->page_incognito() < rhs->page_incognito();
540 if (lhs->page_title() != rhs->page_title())
541 return lhs->page_title() < rhs->page_title();
543 if (lhs->arg_url().spec() != rhs->arg_url().spec())
544 return lhs->arg_url().spec() < rhs->arg_url().spec();
545 if (lhs->arg_incognito() != rhs->arg_incognito())
546 return lhs->arg_incognito() < rhs->arg_incognito();
548 // other is treated much like the args field.
549 if (!lhs->other() && rhs->other())
550 return true;
551 if (lhs->other() && !rhs->other())
552 return false;
553 if (lhs->other() && rhs->other()) {
554 std::string lhs_other = ActivityLogPolicy::Util::Serialize(lhs->other());
555 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other());
556 if (lhs_other != rhs_other)
557 return lhs_other < rhs_other;
560 // All fields compare as equal if this point is reached.
561 return false;
564 } // namespace extensions