Home | History | Annotate | Download | only in activity_log
      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 
     46 UmaPolicy::UmaPolicy(Profile* profile)
     47     : ActivityLogPolicy(profile), profile_(profile) {
     48   DCHECK(!profile->IsOffTheRecord());
     49   BrowserList::AddObserver(this);
     50 }
     51 
     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.
     58 void UmaPolicy::Close() {
     59   delete this;
     60 }
     61 
     62 // Process actions. ------------------------------------------------------------
     63 
     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 
     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 
    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 
    222 void UmaPolicy::OnBrowserAdded(Browser* browser) {
    223   if (!profile_->IsSameProfile(browser->profile()))
    224     return;
    225   browser->tab_strip_model()->AddObserver(this);
    226 }
    227 
    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.
    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.
    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 
    286 void UmaPolicy::SetupOpenedPage(const std::string& url) {
    287   url_status_[url][kNumberOfTabs]++;
    288 }
    289 
    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.
    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 
    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