• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/activity_log/uma_policy.h"
6 
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"
16 
17 namespace {
18 
19 // For convenience.
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;
34 
35 }  // namespace
36 
37 namespace extensions {
38 
39 // Class constants, also used in testing. --------------------------------------
40 
41 const char UmaPolicy::kNumberOfTabs[]       = "num_tabs";
42 const size_t UmaPolicy::kMaxTabsTracked     = 50;
43 
44 // Setup and shutdown. ---------------------------------------------------------
45 
UmaPolicy(Profile * profile)46 UmaPolicy::UmaPolicy(Profile* profile)
47     : ActivityLogPolicy(profile), profile_(profile) {
48   DCHECK(!profile->IsOffTheRecord());
49   BrowserList::AddObserver(this);
50 }
51 
~UmaPolicy()52 UmaPolicy::~UmaPolicy() {
53   BrowserList::RemoveObserver(this);
54 }
55 
56 // Unlike the other policies, UmaPolicy can commit suicide directly because it
57 // doesn't have a dependency on a database.
Close()58 void UmaPolicy::Close() {
59   delete this;
60 }
61 
62 // Process actions. ------------------------------------------------------------
63 
ProcessAction(scoped_refptr<Action> action)64 void UmaPolicy::ProcessAction(scoped_refptr<Action> action) {
65   if (!action->page_url().is_valid() && !action->arg_url().is_valid())
66     return;
67   if (action->page_incognito() || action->arg_incognito())
68     return;
69   std::string url;
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());
76   }
77   if (url.empty())
78     return;
79 
80   SiteMap::iterator site_lookup = url_status_.find(url);
81   if (site_lookup != url_status_.end())
82     site_lookup->second[action->extension_id()] |= status;
83 }
84 
MatchActionToStatus(scoped_refptr<Action> action)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) {
92     return kNoStatus;
93   }
94 
95   int dom_verb;
96   if (!action->other() ||
97       !action->other()->GetIntegerWithoutPathExpansion(
98           activity_log_constants::kActionDomVerb, &dom_verb)) {
99     return kNoStatus;
100   }
101 
102   int ret_bit = kNoStatus;
103   DomActionType::Type dom_type = static_cast<DomActionType::Type>(dom_verb);
104   if (dom_type == DomActionType::GETTER)
105     return kReadDom;
106   if (dom_type == DomActionType::SETTER) {
107     ret_bit |= kModifiedDom;
108   } else if (dom_type == DomActionType::METHOD) {
109     ret_bit |= kDomMethod;
110   } else {
111     return kNoStatus;
112   }
113 
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") {
120     std::string arg;
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;
136     }
137   }
138   return ret_bit;
139 }
140 
HistogramOnClose(const std::string & url)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/")
144     return;
145 
146     int statuses[MAX_STATUS-1];
147   std::memset(statuses, 0, sizeof(statuses));
148 
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)
154       continue;
155     for (int i = NONE + 1; i < MAX_STATUS; ++i) {
156       if (ext_iter->second & (1 << i))
157         statuses[i-1]++;
158     }
159   }
160 
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]);
189   } else {
190     prefix += "Google.";
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]);
217   }
218 }
219 
220 // Handle tab tracking. --------------------------------------------------------
221 
OnBrowserAdded(Browser * browser)222 void UmaPolicy::OnBrowserAdded(Browser* browser) {
223   if (!profile_->IsSameProfile(browser->profile()))
224     return;
225   browser->tab_strip_model()->AddObserver(this);
226 }
227 
OnBrowserRemoved(Browser * browser)228 void UmaPolicy::OnBrowserRemoved(Browser* browser) {
229   if (!profile_->IsSameProfile(browser->profile()))
230     return;
231   browser->tab_strip_model()->RemoveObserver(this);
232 }
233 
234 // Use the value from SessionID::IdForTab, *not* |index|. |index| will be
235 // duplicated across tabs in a session, whereas IdForTab uniquely identifies
236 // each tab.
TabChangedAt(content::WebContents * contents,int index,TabChangeType change_type)237 void UmaPolicy::TabChangedAt(content::WebContents* contents,
238                              int index,
239                              TabChangeType change_type) {
240   if (change_type != TabStripModelObserver::LOADING_ONLY)
241     return;
242   if (!contents)
243     return;
244 
245   std::string url = CleanURL(contents->GetLastCommittedURL());
246   int32 tab_id = SessionID::IdForTab(contents);
247 
248   std::map<int32, std::string>::iterator tab_it = tab_list_.find(tab_id);
249 
250   // Ignore tabs that haven't changed status.
251   if (tab_it != tab_list_.end() && tab_it->second == url)
252     return;
253 
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);
258   }
259 
260   // Check that tab_list_ isn't over the kMaxTabsTracked budget.
261   if (tab_list_.size() >= kMaxTabsTracked)
262     return;
263 
264   // Set up the new entries.
265   tab_list_[tab_id] = url;
266   SetupOpenedPage(url);
267 }
268 
269 // Use the value from SessionID::IdForTab, *not* |index|. |index| will be
270 // duplicated across tabs in a session, whereas IdForTab uniquely identifies
271 // each tab.
TabClosingAt(TabStripModel * tab_strip_model,content::WebContents * contents,int index)272 void UmaPolicy::TabClosingAt(TabStripModel* tab_strip_model,
273                              content::WebContents* contents,
274                              int index) {
275   if (!contents)
276     return;
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);
282 
283   CleanupClosedPage(url);
284 }
285 
SetupOpenedPage(const std::string & url)286 void UmaPolicy::SetupOpenedPage(const std::string& url) {
287   url_status_[url][kNumberOfTabs]++;
288 }
289 
CleanupClosedPage(const std::string & url)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())
293     return;
294   old_site_lookup->second[kNumberOfTabs]--;
295   if (old_site_lookup->second[kNumberOfTabs] == 0) {
296     HistogramOnClose(url);
297     url_status_.erase(url);
298   }
299 }
300 
301 // Helpers. --------------------------------------------------------------------
302 
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.
CleanURL(const GURL & gurl)306 std::string UmaPolicy::CleanURL(const GURL& gurl) {
307   if (gurl.spec().empty())
308     return GURL("about:blank").spec();
309   if (!gurl.is_valid())
310     return gurl.spec();
311   if (!gurl.has_ref())
312     return gurl.spec();
313   std::string port = "";
314   if (gurl.has_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(),
321                             gurl.host().c_str(),
322                             port.c_str(),
323                             gurl.path().c_str(),
324                             query.c_str());
325 }
326 
GetHistogramName(PageStatus status)327 const char* UmaPolicy::GetHistogramName(PageStatus status) {
328   switch (status) {
329     case CONTENT_SCRIPT:
330       return "ContentScript";
331     case READ_DOM:
332       return "ReadDom";
333     case MODIFIED_DOM:
334       return "ModifiedDom";
335     case DOM_METHOD:
336       return "InvokedDomMethod";
337     case DOCUMENT_WRITE:
338       return "DocumentWrite";
339     case INNER_HTML:
340       return "InnerHtml";
341     case CREATED_SCRIPT:
342       return "CreatedScript";
343     case CREATED_IFRAME:
344       return "CreatedIframe";
345     case CREATED_DIV:
346       return "CreatedDiv";
347     case CREATED_LINK:
348       return "CreatedLink";
349     case CREATED_INPUT:
350       return "CreatedInput";
351     case CREATED_EMBED:
352       return "CreatedEmbed";
353     case CREATED_OBJECT:
354       return "CreatedObject";
355     case NONE:
356     case MAX_STATUS:
357     default:
358       NOTREACHED();
359       return "";
360   }
361 }
362 
363 }  // namespace extensions
364