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

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

Issue 292313006: Improve activity log ad metrics (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 7 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 "chrome/browser/extensions/activity_log/activity_actions.h" 5 #include "chrome/browser/extensions/activity_log/activity_actions.h"
6 6
7 #include <algorithm> // for std::find. 7 #include <algorithm> // for std::find.
8 #include <string> 8 #include <string>
9 9
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/format_macros.h" 11 #include "base/format_macros.h"
12 #include "base/json/json_string_value_serializer.h" 12 #include "base/json/json_string_value_serializer.h"
13 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/macros.h" 14 #include "base/macros.h"
15 #include "base/memory/singleton.h" 15 #include "base/memory/singleton.h"
16 #include "base/metrics/histogram.h"
16 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h" 18 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h" 19 #include "base/strings/stringprintf.h"
19 #include "base/values.h" 20 #include "base/values.h"
20 #include "chrome/browser/extensions/activity_log/activity_action_constants.h" 21 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
21 #include "chrome/browser/extensions/activity_log/ad_network_database.h" 22 #include "chrome/browser/extensions/activity_log/ad_network_database.h"
22 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" 23 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
23 #include "chrome/browser/ui/browser.h" 24 #include "chrome/browser/ui/browser.h"
24 #include "chrome/common/chrome_switches.h" 25 #include "chrome/common/chrome_switches.h"
25 #include "components/rappor/rappor_service.h" 26 #include "components/rappor/rappor_service.h"
26 #include "content/public/browser/web_contents.h" 27 #include "content/public/browser/web_contents.h"
27 #include "extensions/common/ad_injection_constants.h" 28 #include "extensions/common/ad_injection_constants.h"
28 #include "extensions/common/constants.h" 29 #include "extensions/common/constants.h"
29 #include "extensions/common/dom_action_types.h" 30 #include "extensions/common/dom_action_types.h"
30 #include "sql/statement.h" 31 #include "sql/statement.h"
31 #include "url/gurl.h" 32 #include "url/gurl.h"
32 33
33 namespace constants = activity_log_constants; 34 namespace constants = activity_log_constants;
34 35
35 namespace extensions { 36 namespace extensions {
36 37
37 namespace { 38 namespace {
38 39
39 namespace keys = ad_injection_constants::keys; 40 namespace keys = ad_injection_constants::keys;
40 41
41 // The list of APIs for which we upload the URL to RAPPOR. 42 // The list of APIs for which we upload the URL to RAPPOR.
42 const char* kApisForRapporMetric[] = { 43 const char* kApisForRapporMetric[] = {
43 "HTMLIFrameElement.src", 44 ad_injection_constants::kHtmlIframeSrcApiName,
44 "HTMLEmbedElement.src", 45 ad_injection_constants::kHtmlEmbedSrcApiName,
45 "HTMLAnchorElement.href", 46 ad_injection_constants::kHtmlAnchorHrefApiName
46 }; 47 };
47 48
48 const char* kExtensionAdInjectionRapporMetricName = 49 const char* kExtensionAdInjectionRapporMetricName =
49 "Extensions.PossibleAdInjection"; 50 "Extensions.PossibleAdInjection";
50 51
51 // The elements for which we check the 'src' attribute to look for ads. 52 // The names of different types of HTML elements we check for ad injection.
52 const char* kSrcElements[] = { 53 const char* kIframeElementType = "HTMLIFrameElement";
53 "HTMLIFrameElement", 54 const char* kEmbedElementType = "HTMLEmbedElement";
54 "HTMLEmbedElement" 55 const char* kAnchorElementType = "HTMLAnchorElement";
55 };
56
57 // The elements for which we check the 'href' attribute to look for ads.
58 const char* kHrefElements[] = {
59 "HTMLAnchorElement",
60 };
61
62 bool IsSrcElement(const std::string& str) {
63 static const char** end = kSrcElements + arraysize(kSrcElements);
64 return std::find(kSrcElements, end, str) != end;
65 }
66
67 bool IsHrefElement(const std::string& str) {
68 static const char** end = kHrefElements + arraysize(kHrefElements);
69 return std::find(kHrefElements, end, str) != end;
70 }
71 56
72 std::string Serialize(const base::Value* value) { 57 std::string Serialize(const base::Value* value) {
73 std::string value_as_text; 58 std::string value_as_text;
74 if (!value) { 59 if (!value) {
75 value_as_text = "null"; 60 value_as_text = "null";
76 } else { 61 } else {
77 JSONStringValueSerializer serializer(&value_as_text); 62 JSONStringValueSerializer serializer(&value_as_text);
78 serializer.SerializeAndOmitBinaryValues(*value); 63 serializer.SerializeAndOmitBinaryValues(*value);
79 } 64 }
80 return value_as_text; 65 return value_as_text;
81 } 66 }
82 67
83 Action::InjectionType CheckDomObject(const base::DictionaryValue* object) {
84 std::string type;
85 object->GetString(keys::kType, &type);
86
87 std::string url_key;
88 if (IsSrcElement(type))
89 url_key = keys::kSrc;
90 else if (IsHrefElement(type))
91 url_key = keys::kHref;
92
93 if (!url_key.empty()) {
94 std::string url;
95 if (object->GetString(url_key, &url)) {
96 GURL gurl(url);
97 if (AdNetworkDatabase::Get()->IsAdNetwork(gurl))
98 return Action::INJECTION_NEW_AD;
99 // If the extension injected an URL which is not local to itself, there is
100 // a good chance it could be a new ad, and our database missed it.
101 // This could be noisier than other metrics, because there are perfectly
102 // acceptable uses for this, like "Show my mail".
103 if (gurl.is_valid() &&
104 !gurl.is_empty() &&
105 !gurl.SchemeIs(kExtensionScheme)) {
106 return Action::INJECTION_LIKELY_NEW_AD;
107 }
108 }
109 }
110
111 const base::ListValue* children = NULL;
112 if (object->GetList(keys::kChildren, &children)) {
113 const base::DictionaryValue* child = NULL;
114 for (size_t i = 0;
115 i < children->GetSize() &&
116 i < ad_injection_constants::kMaximumChildrenToCheck;
117 ++i) {
118 if (children->GetDictionary(i, &child)) {
119 Action::InjectionType type = CheckDomObject(child);
120 if (type != Action::NO_AD_INJECTION)
121 return type;
122 }
123 }
124 }
125
126 return Action::NO_AD_INJECTION;
127 }
128
129 } // namespace 68 } // namespace
130 69
131 using api::activity_log_private::ExtensionActivity; 70 using api::activity_log_private::ExtensionActivity;
132 71
133 Action::Action(const std::string& extension_id, 72 Action::Action(const std::string& extension_id,
134 const base::Time& time, 73 const base::Time& time,
135 const ActionType action_type, 74 const ActionType action_type,
136 const std::string& api_name, 75 const std::string& api_name,
137 int64 action_id) 76 int64 action_id)
138 : extension_id_(extension_id), 77 : extension_id_(extension_id),
(...skipping 28 matching lines...) Expand all
167 106
168 Action::InjectionType Action::DidInjectAd( 107 Action::InjectionType Action::DidInjectAd(
169 rappor::RapporService* rappor_service) const { 108 rappor::RapporService* rappor_service) const {
170 MaybeUploadUrl(rappor_service); 109 MaybeUploadUrl(rappor_service);
171 110
172 // Currently, we do not have the list of ad networks, so we exit immediately 111 // Currently, we do not have the list of ad networks, so we exit immediately
173 // with NO_AD_INJECTION (unless the database has been set by a test). 112 // with NO_AD_INJECTION (unless the database has been set by a test).
174 if (!AdNetworkDatabase::Get()) 113 if (!AdNetworkDatabase::Get())
175 return NO_AD_INJECTION; 114 return NO_AD_INJECTION;
176 115
177 if (api_name_ == ad_injection_constants::kHtmlIframeSrcApiName || 116 AdType ad_type = AD_TYPE_NONE;
178 api_name_ == ad_injection_constants::kHtmlEmbedSrcApiName || 117 InjectionType injection_type = NO_AD_INJECTION;
179 api_name_ == ad_injection_constants::kHtmlAnchorHrefApiName) { 118 if (api_name_ == ad_injection_constants::kHtmlIframeSrcApiName)
180 return CheckSrcModification(); 119 ad_type = AD_TYPE_IFRAME;
120 else if (api_name_ == ad_injection_constants::kHtmlEmbedSrcApiName)
121 ad_type = AD_TYPE_EMBED;
122 else if (api_name_ == ad_injection_constants::kHtmlAnchorHrefApiName)
123 ad_type = AD_TYPE_ANCHOR;
124
125 if (ad_type != AD_TYPE_NONE) { // All the above apis modify an element's src.
felt 2014/05/24 00:07:10 nit: does it make sense to flip this else/if condi
Devlin 2014/05/27 16:05:16 Switched 'em up in a way that I think makes it mor
126 injection_type = CheckSrcModification();
181 } else if (EndsWith(api_name_, 127 } else if (EndsWith(api_name_,
182 ad_injection_constants::kAppendChildApiSuffix, 128 ad_injection_constants::kAppendChildApiSuffix,
183 true /* case senstive */)) { 129 true /* case senstive */)) {
184 return CheckAppendChild(); 130 injection_type = CheckAppendChild(&ad_type);
185 } 131 }
186 132
187 return NO_AD_INJECTION; 133 if (injection_type != NO_AD_INJECTION) {
134 UMA_HISTOGRAM_ENUMERATION(
135 "Extensions.AdInjection.Type", ad_type, Action::NUM_AD_TYPES);
136 }
137
138 return injection_type;
188 } 139 }
189 140
190 void Action::set_args(scoped_ptr<base::ListValue> args) { 141 void Action::set_args(scoped_ptr<base::ListValue> args) {
191 args_.reset(args.release()); 142 args_.reset(args.release());
192 } 143 }
193 144
194 base::ListValue* Action::mutable_args() { 145 base::ListValue* Action::mutable_args() {
195 if (!args_.get()) { 146 if (!args_.get()) {
196 args_.reset(new base::ListValue()); 147 args_.reset(new base::ListValue());
197 } 148 }
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after
396 result += " ARG_URL=" + arg_url_.spec(); 347 result += " ARG_URL=" + arg_url_.spec();
397 } 348 }
398 if (other_.get()) { 349 if (other_.get()) {
399 result += " OTHER=" + Serialize(other_.get()); 350 result += " OTHER=" + Serialize(other_.get());
400 } 351 }
401 352
402 result += base::StringPrintf(" COUNT=%d", count_); 353 result += base::StringPrintf(" COUNT=%d", count_);
403 return result; 354 return result;
404 } 355 }
405 356
357 bool Action::UrlCouldBeAd(const GURL& url) const {
felt 2014/05/24 00:07:10 this is a good idea
358 // Ads can only be valid urls that don't match the page's host (linking to the
359 // current page should be considered valid use), and aren't local to the
360 // extension.
361 return url.is_valid() &&
362 !url.is_empty() &&
363 url.host() != page_url_.host() &&
364 !url.SchemeIs(kExtensionScheme);
365 }
366
406 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const { 367 void Action::MaybeUploadUrl(rappor::RapporService* rappor_service) const {
407 // If there's no given |rappor_service|, abort immediately. 368 // Don't bother recording if the url is innocuous (or no |rappor_service|).
408 if (!rappor_service) 369 if (!rappor_service || !UrlCouldBeAd(arg_url_))
409 return;
410
411 // If the action has no url, or the url is empty, then return.
412 if (!arg_url_.is_valid() || arg_url_.is_empty())
413 return;
414 std::string host = arg_url_.host();
415 if (host.empty())
416 return; 370 return;
417 371
418 bool can_inject_ads = false; 372 bool can_inject_ads = false;
419 for (size_t i = 0; i < arraysize(kApisForRapporMetric); ++i) { 373 for (size_t i = 0; i < arraysize(kApisForRapporMetric); ++i) {
420 if (api_name_ == kApisForRapporMetric[i]) { 374 if (api_name_ == kApisForRapporMetric[i]) {
421 can_inject_ads = true; 375 can_inject_ads = true;
422 break; 376 break;
423 } 377 }
424 } 378 }
425 379
426 if (!can_inject_ads) 380 if (!can_inject_ads)
427 return; 381 return;
428 382
429 // Record the URL - an ad *may* have been injected. 383 // Record the URL - an ad *may* have been injected.
430 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName, 384 rappor_service->RecordSample(kExtensionAdInjectionRapporMetricName,
431 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, 385 rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
432 host); 386 arg_url_.host());
433 } 387 }
434 388
435 Action::InjectionType Action::CheckSrcModification() const { 389 Action::InjectionType Action::CheckSrcModification() const {
436 const AdNetworkDatabase* database = AdNetworkDatabase::Get(); 390 const AdNetworkDatabase* database = AdNetworkDatabase::Get();
437 391
438 bool arg_url_valid = arg_url_.is_valid() && !arg_url_.is_empty(); 392 bool arg_url_could_be_ad = UrlCouldBeAd(arg_url_);
439 393
440 GURL prev_url; 394 GURL prev_url;
441 std::string prev_url_string; 395 std::string prev_url_string;
442 if (args_.get() && args_->GetString(1u, &prev_url_string)) 396 if (args_.get() && args_->GetString(1u, &prev_url_string))
443 prev_url = GURL(prev_url_string); 397 prev_url = GURL(prev_url_string);
444 398
445 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty(); 399 bool prev_url_valid = prev_url.is_valid() && !prev_url.is_empty();
446 400
447 bool injected_ad = arg_url_valid && database->IsAdNetwork(arg_url_); 401 bool injected_ad = arg_url_could_be_ad && database->IsAdNetwork(arg_url_);
448 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url); 402 bool replaced_ad = prev_url_valid && database->IsAdNetwork(prev_url);
449 403
450 if (injected_ad && replaced_ad) 404 if (injected_ad && replaced_ad)
451 return INJECTION_REPLACED_AD; 405 return INJECTION_REPLACED_AD;
452 if (injected_ad) 406 if (injected_ad)
453 return INJECTION_NEW_AD; 407 return INJECTION_NEW_AD;
454 if (replaced_ad) 408 if (replaced_ad)
455 return INJECTION_REMOVED_AD; 409 return INJECTION_REMOVED_AD;
456 410
457 // If the extension modified the URL with an external, valid URL then there's 411 // If the extension modified the URL with an external, valid URL then there's
458 // a good chance it's ad injection. Log it as a likely one, which also helps 412 // a good chance it's ad injection. Log it as a likely one, which also helps
459 // us determine the effectiveness of our IsAdNetwork() recognition. 413 // us determine the effectiveness of our IsAdNetwork() recognition.
460 if (arg_url_valid && !arg_url_.SchemeIs(kExtensionScheme)) { 414 if (arg_url_could_be_ad) {
461 if (prev_url_valid) 415 if (prev_url_valid)
462 return INJECTION_LIKELY_REPLACED_AD; 416 return INJECTION_LIKELY_REPLACED_AD;
463 return INJECTION_LIKELY_NEW_AD; 417 return INJECTION_LIKELY_NEW_AD;
464 } 418 }
465 419
466 return NO_AD_INJECTION; 420 return NO_AD_INJECTION;
467 } 421 }
468 422
469 Action::InjectionType Action::CheckAppendChild() const { 423 Action::InjectionType Action::CheckAppendChild(AdType* ad_type_out) const {
470 const base::DictionaryValue* child = NULL; 424 const base::DictionaryValue* child = NULL;
471 if (!args_->GetDictionary(0u, &child)) 425 if (!args_->GetDictionary(0u, &child))
472 return NO_AD_INJECTION; 426 return NO_AD_INJECTION;
473 427
474 return CheckDomObject(child); 428 return CheckDomObject(child, ad_type_out);
429 }
430
431 Action::InjectionType Action::CheckDomObject(
432 const base::DictionaryValue* object,
433 AdType* ad_type_out) const {
434 DCHECK(ad_type_out);
435 std::string type;
436 object->GetString(keys::kType, &type);
437
438 AdType ad_type = AD_TYPE_NONE;
439 std::string url_key;
440 if (type == kIframeElementType) {
441 ad_type = AD_TYPE_IFRAME;
442 url_key = keys::kSrc;
443 } else if (type == kEmbedElementType) {
444 ad_type = AD_TYPE_EMBED;
445 url_key = keys::kSrc;
446 } else if (type == kAnchorElementType) {
447 ad_type = AD_TYPE_ANCHOR;
448 url_key = keys::kHref;
449 }
450
451 if (!url_key.empty()) {
452 std::string url;
453 if (object->GetString(url_key, &url)) {
454 GURL gurl(url);
455 if (UrlCouldBeAd(gurl)) {
456 *ad_type_out = ad_type;
457 if (AdNetworkDatabase::Get()->IsAdNetwork(gurl))
458 return INJECTION_NEW_AD;
459 // If the extension injected an URL which is not local to itself or the
460 // page, there is a good chance it could be a new ad, and our database
461 // missed it.
462 return INJECTION_LIKELY_NEW_AD;
463 }
464 }
465 }
466
467 const base::ListValue* children = NULL;
468 if (object->GetList(keys::kChildren, &children)) {
469 const base::DictionaryValue* child = NULL;
470 for (size_t i = 0;
471 i < children->GetSize() &&
472 i < ad_injection_constants::kMaximumChildrenToCheck;
473 ++i) {
474 if (children->GetDictionary(i, &child)) {
475 InjectionType type = CheckDomObject(child, ad_type_out);
476 if (type != NO_AD_INJECTION)
477 return type;
478 }
479 }
480 }
481
482 return NO_AD_INJECTION;
475 } 483 }
476 484
477 bool ActionComparator::operator()( 485 bool ActionComparator::operator()(
478 const scoped_refptr<Action>& lhs, 486 const scoped_refptr<Action>& lhs,
479 const scoped_refptr<Action>& rhs) const { 487 const scoped_refptr<Action>& rhs) const {
480 if (lhs->time() != rhs->time()) 488 if (lhs->time() != rhs->time())
481 return lhs->time() < rhs->time(); 489 return lhs->time() < rhs->time();
482 else if (lhs->action_id() != rhs->action_id()) 490 else if (lhs->action_id() != rhs->action_id())
483 return lhs->action_id() < rhs->action_id(); 491 return lhs->action_id() < rhs->action_id();
484 else 492 else
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
532 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other()); 540 std::string rhs_other = ActivityLogPolicy::Util::Serialize(rhs->other());
533 if (lhs_other != rhs_other) 541 if (lhs_other != rhs_other)
534 return lhs_other < rhs_other; 542 return lhs_other < rhs_other;
535 } 543 }
536 544
537 // All fields compare as equal if this point is reached. 545 // All fields compare as equal if this point is reached.
538 return false; 546 return false;
539 } 547 }
540 548
541 } // namespace extensions 549 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/activity_log/activity_actions.h ('k') | chrome/browser/extensions/activity_log/uma_policy.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698