1 // Copyright 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/uma_policy.h"
7 #include "base/metrics/histogram.h"
8 #include "base/strings/stringprintf.h"
9 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
10 #include "chrome/browser/sessions/session_id.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_list.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/common/extensions/dom_action_types.h"
15 #include "content/public/browser/web_contents.h"
20 const int kNoStatus
= extensions::UmaPolicy::NONE
;
21 const int kContentScript
= 1 << extensions::UmaPolicy::CONTENT_SCRIPT
;
22 const int kReadDom
= 1 << extensions::UmaPolicy::READ_DOM
;
23 const int kModifiedDom
= 1 << extensions::UmaPolicy::MODIFIED_DOM
;
24 const int kDomMethod
= 1 << extensions::UmaPolicy::DOM_METHOD
;
25 const int kDocumentWrite
= 1 << extensions::UmaPolicy::DOCUMENT_WRITE
;
26 const int kInnerHtml
= 1 << extensions::UmaPolicy::INNER_HTML
;
27 const int kCreatedScript
= 1 << extensions::UmaPolicy::CREATED_SCRIPT
;
28 const int kCreatedIframe
= 1 << extensions::UmaPolicy::CREATED_IFRAME
;
29 const int kCreatedDiv
= 1 << extensions::UmaPolicy::CREATED_DIV
;
30 const int kCreatedLink
= 1 << extensions::UmaPolicy::CREATED_LINK
;
31 const int kCreatedInput
= 1 << extensions::UmaPolicy::CREATED_INPUT
;
32 const int kCreatedEmbed
= 1 << extensions::UmaPolicy::CREATED_EMBED
;
33 const int kCreatedObject
= 1 << extensions::UmaPolicy::CREATED_OBJECT
;
37 namespace extensions
{
39 // Class constants, also used in testing. --------------------------------------
41 const char UmaPolicy::kNumberOfTabs
[] = "num_tabs";
42 const size_t UmaPolicy::kMaxTabsTracked
= 50;
44 // Setup and shutdown. ---------------------------------------------------------
46 UmaPolicy::UmaPolicy(Profile
* profile
)
47 : ActivityLogPolicy(profile
), profile_(profile
) {
48 DCHECK(!profile
->IsOffTheRecord());
49 BrowserList::AddObserver(this);
52 UmaPolicy::~UmaPolicy() {
53 BrowserList::RemoveObserver(this);
56 // Unlike the other policies, UmaPolicy can commit suicide directly because it
57 // doesn't have a dependency on a database.
58 void UmaPolicy::Close() {
62 // Process actions. ------------------------------------------------------------
64 void UmaPolicy::ProcessAction(scoped_refptr
<Action
> action
) {
65 if (!action
->page_url().is_valid() && !action
->arg_url().is_valid())
67 if (action
->page_incognito() || action
->arg_incognito())
70 int status
= MatchActionToStatus(action
);
71 if (action
->page_url().is_valid()) {
72 url
= CleanURL(action
->page_url());
73 } else if (status
& kContentScript
) {
74 // This is for the tabs.executeScript case.
75 url
= CleanURL(action
->arg_url());
80 SiteMap::iterator site_lookup
= url_status_
.find(url
);
81 if (site_lookup
!= url_status_
.end())
82 site_lookup
->second
[action
->extension_id()] |= status
;
85 int UmaPolicy::MatchActionToStatus(scoped_refptr
<Action
> action
) {
86 if (action
->action_type() == Action::ACTION_CONTENT_SCRIPT
) {
87 return kContentScript
;
88 } else if (action
->action_type() == Action::ACTION_API_CALL
&&
89 action
->api_name() == "tabs.executeScript") {
90 return kContentScript
;
91 } else if (action
->action_type() != Action::ACTION_DOM_ACCESS
) {
96 if (!action
->other() ||
97 !action
->other()->GetIntegerWithoutPathExpansion(
98 activity_log_constants::kActionDomVerb
, &dom_verb
)) {
102 int ret_bit
= kNoStatus
;
103 DomActionType::Type dom_type
= static_cast<DomActionType::Type
>(dom_verb
);
104 if (dom_type
== DomActionType::GETTER
)
106 if (dom_type
== DomActionType::SETTER
) {
107 ret_bit
|= kModifiedDom
;
108 } else if (dom_type
== DomActionType::METHOD
) {
109 ret_bit
|= kDomMethod
;
114 if (action
->api_name() == "HTMLDocument.write" ||
115 action
->api_name() == "HTMLDocument.writeln") {
116 ret_bit
|= kDocumentWrite
;
117 } else if (action
->api_name() == "Element.innerHTML") {
118 ret_bit
|= kInnerHtml
;
119 } else if (action
->api_name() == "Document.createElement") {
121 action
->args()->GetString(0, &arg
);
122 if (arg
== "script") {
123 ret_bit
|= kCreatedScript
;
124 } else if (arg
== "iframe") {
125 ret_bit
|= kCreatedIframe
;
126 } else if (arg
== "div") {
127 ret_bit
|= kCreatedDiv
;
128 } else if (arg
== "a") {
129 ret_bit
|= kCreatedLink
;
130 } else if (arg
== "input") {
131 ret_bit
|= kCreatedInput
;
132 } else if (arg
== "embed") {
133 ret_bit
|= kCreatedEmbed
;
134 } else if (arg
== "object") {
135 ret_bit
|= kCreatedObject
;
141 void UmaPolicy::HistogramOnClose(const std::string
& url
) {
142 // Let's try to avoid histogramming useless URLs.
143 if (url
== "about:blank" || url
.empty() || url
== "chrome://newtab/")
146 int statuses
[MAX_STATUS
-1];
147 std::memset(statuses
, 0, sizeof(statuses
));
149 SiteMap::iterator site_lookup
= url_status_
.find(url
);
150 ExtensionMap exts
= site_lookup
->second
;
151 ExtensionMap::iterator ext_iter
;
152 for (ext_iter
= exts
.begin(); ext_iter
!= exts
.end(); ++ext_iter
) {
153 if (ext_iter
->first
== kNumberOfTabs
)
155 for (int i
= NONE
+ 1; i
< MAX_STATUS
; ++i
) {
156 if (ext_iter
->second
& (1 << i
))
161 std::string prefix
= "ExtensionActivity.";
162 if (GURL(url
).host() != "www.google.com") {
163 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CONTENT_SCRIPT
),
164 statuses
[CONTENT_SCRIPT
- 1]);
165 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(READ_DOM
),
166 statuses
[READ_DOM
- 1]);
167 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(MODIFIED_DOM
),
168 statuses
[MODIFIED_DOM
- 1]);
169 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(DOM_METHOD
),
170 statuses
[DOM_METHOD
- 1]);
171 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(DOCUMENT_WRITE
),
172 statuses
[DOCUMENT_WRITE
- 1]);
173 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(INNER_HTML
),
174 statuses
[INNER_HTML
- 1]);
175 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_SCRIPT
),
176 statuses
[CREATED_SCRIPT
- 1]);
177 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_IFRAME
),
178 statuses
[CREATED_IFRAME
- 1]);
179 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_DIV
),
180 statuses
[CREATED_DIV
- 1]);
181 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_LINK
),
182 statuses
[CREATED_LINK
- 1]);
183 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_INPUT
),
184 statuses
[CREATED_INPUT
- 1]);
185 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_EMBED
),
186 statuses
[CREATED_EMBED
- 1]);
187 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_OBJECT
),
188 statuses
[CREATED_OBJECT
- 1]);
191 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CONTENT_SCRIPT
),
192 statuses
[CONTENT_SCRIPT
- 1]);
193 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(READ_DOM
),
194 statuses
[READ_DOM
- 1]);
195 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(MODIFIED_DOM
),
196 statuses
[MODIFIED_DOM
- 1]);
197 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(DOM_METHOD
),
198 statuses
[DOM_METHOD
- 1]);
199 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(DOCUMENT_WRITE
),
200 statuses
[DOCUMENT_WRITE
- 1]);
201 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(INNER_HTML
),
202 statuses
[INNER_HTML
- 1]);
203 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_SCRIPT
),
204 statuses
[CREATED_SCRIPT
- 1]);
205 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_IFRAME
),
206 statuses
[CREATED_IFRAME
- 1]);
207 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_DIV
),
208 statuses
[CREATED_DIV
- 1]);
209 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_LINK
),
210 statuses
[CREATED_LINK
- 1]);
211 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_INPUT
),
212 statuses
[CREATED_INPUT
- 1]);
213 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_EMBED
),
214 statuses
[CREATED_EMBED
- 1]);
215 UMA_HISTOGRAM_COUNTS_100(prefix
+ GetHistogramName(CREATED_OBJECT
),
216 statuses
[CREATED_OBJECT
- 1]);
220 // Handle tab tracking. --------------------------------------------------------
222 void UmaPolicy::OnBrowserAdded(Browser
* browser
) {
223 if (!profile_
->IsSameProfile(browser
->profile()))
225 browser
->tab_strip_model()->AddObserver(this);
228 void UmaPolicy::OnBrowserRemoved(Browser
* browser
) {
229 if (!profile_
->IsSameProfile(browser
->profile()))
231 browser
->tab_strip_model()->RemoveObserver(this);
234 // Use the value from SessionID::IdForTab, *not* |index|. |index| will be
235 // duplicated across tabs in a session, whereas IdForTab uniquely identifies
237 void UmaPolicy::TabChangedAt(content::WebContents
* contents
,
239 TabChangeType change_type
) {
240 if (change_type
!= TabStripModelObserver::LOADING_ONLY
)
245 std::string url
= CleanURL(contents
->GetLastCommittedURL());
246 int32 tab_id
= SessionID::IdForTab(contents
);
248 std::map
<int32
, std::string
>::iterator tab_it
= tab_list_
.find(tab_id
);
250 // Ignore tabs that haven't changed status.
251 if (tab_it
!= tab_list_
.end() && tab_it
->second
== url
)
254 // Is this an existing tab whose URL has changed.
255 if (tab_it
!= tab_list_
.end()) {
256 CleanupClosedPage(tab_it
->second
);
257 tab_list_
.erase(tab_id
);
260 // Check that tab_list_ isn't over the kMaxTabsTracked budget.
261 if (tab_list_
.size() >= kMaxTabsTracked
)
264 // Set up the new entries.
265 tab_list_
[tab_id
] = url
;
266 SetupOpenedPage(url
);
269 // Use the value from SessionID::IdForTab, *not* |index|. |index| will be
270 // duplicated across tabs in a session, whereas IdForTab uniquely identifies
272 void UmaPolicy::TabClosingAt(TabStripModel
* tab_strip_model
,
273 content::WebContents
* contents
,
277 std::string url
= CleanURL(contents
->GetLastCommittedURL());
278 int32 tab_id
= SessionID::IdForTab(contents
);
279 std::map
<int, std::string
>::iterator tab_it
= tab_list_
.find(tab_id
);
280 if (tab_it
!= tab_list_
.end())
281 tab_list_
.erase(tab_id
);
283 CleanupClosedPage(url
);
286 void UmaPolicy::SetupOpenedPage(const std::string
& url
) {
287 url_status_
[url
][kNumberOfTabs
]++;
290 void UmaPolicy::CleanupClosedPage(const std::string
& url
) {
291 SiteMap::iterator old_site_lookup
= url_status_
.find(url
);
292 if (old_site_lookup
== url_status_
.end())
294 old_site_lookup
->second
[kNumberOfTabs
]--;
295 if (old_site_lookup
->second
[kNumberOfTabs
] == 0) {
296 HistogramOnClose(url
);
297 url_status_
.erase(url
);
301 // Helpers. --------------------------------------------------------------------
303 // We don't want to treat # ref navigations as if they were new pageloads.
304 // So we get rid of the ref if it has it.
305 // We convert to a string in the hopes that this is faster than Replacements.
306 std::string
UmaPolicy::CleanURL(const GURL
& gurl
) {
307 if (gurl
.spec().empty())
308 return GURL("about:blank").spec();
309 if (!gurl
.is_valid())
313 std::string port
= "";
315 port
= ":" + gurl
.port();
316 std::string query
= "";
317 if (gurl
.has_query())
318 query
= "?" + gurl
.query();
319 return base::StringPrintf("%s://%s%s%s%s",
320 gurl
.scheme().c_str(),
327 const char* UmaPolicy::GetHistogramName(PageStatus status
) {
330 return "ContentScript";
334 return "ModifiedDom";
336 return "InvokedDomMethod";
338 return "DocumentWrite";
342 return "CreatedScript";
344 return "CreatedIframe";
348 return "CreatedLink";
350 return "CreatedInput";
352 return "CreatedEmbed";
354 return "CreatedObject";
363 } // namespace extensions