summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfelt@chromium.org <felt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-23 17:18:37 +0000
committerfelt@chromium.org <felt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-23 17:18:37 +0000
commit29507aa84c0ad8166a8d0737efe8a05b2cf151ea (patch)
tree757a86cadcfb213243a4c5be7542ee4934f59743
parentbc024add15f721c18638fb5485ba4785fc69cdca (diff)
downloadchromium_src-29507aa84c0ad8166a8d0737efe8a05b2cf151ea.zip
chromium_src-29507aa84c0ad8166a8d0737efe8a05b2cf151ea.tar.gz
chromium_src-29507aa84c0ad8166a8d0737efe8a05b2cf151ea.tar.bz2
This CL's goal is to answer the following question: "For each pageload, how many extensions do X thing to the page's DOM?"
The new UmaPolicy class processes ActivityLog actions and selectively histograms "interesting" actions. In order to avoid duplicate counting, the UmaPolicy class keeps track of open tabs & the extensions that operate on them. It records to the histograms when a page is closed. Page are considered "closed" when the user closes a tab or navigates to a new page in the same gab. The UmaPolicy class does not replace the existing ActivityLog database policies. Instead, they co-exist. For this reason there are some small changes in activity_log.cc|h and related tests to distinguish between the database policies and the UMA policy. For privacy reasons, the Activity Log does not initialize the UmaPolicy for incognito profiles. This CL also includes unit tests and browser tests for UmaPolicy. BUG=273895 NOTRY=true Review URL: https://chromiumcodereview.appspot.com/24212002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224734 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/activity_log/activity_log.cc75
-rw-r--r--chrome/browser/extensions/activity_log/activity_log.h25
-rw-r--r--chrome/browser/extensions/activity_log/activity_log_policy.h49
-rw-r--r--chrome/browser/extensions/activity_log/activity_log_unittest.cc4
-rw-r--r--chrome/browser/extensions/activity_log/counting_policy_unittest.cc14
-rw-r--r--chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc22
-rw-r--r--chrome/browser/extensions/activity_log/uma_policy.cc363
-rw-r--r--chrome/browser/extensions/activity_log/uma_policy.h123
-rw-r--r--chrome/browser/extensions/activity_log/uma_policy_browsertest.cc302
-rw-r--r--chrome/browser/extensions/activity_log/uma_policy_unittest.cc225
-rw-r--r--chrome/chrome_browser_extensions.gypi3
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/test/data/extensions/api_test/uma_policy/background.js34
-rw-r--r--chrome/test/data/extensions/api_test/uma_policy/content_script.js38
-rw-r--r--chrome/test/data/extensions/api_test/uma_policy/manifest.json20
-rw-r--r--tools/metrics/histograms/histograms.xml177
17 files changed, 1394 insertions, 82 deletions
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc
index 2eda4bd..f1a0877 100644
--- a/chrome/browser/extensions/activity_log/activity_log.cc
+++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -36,6 +36,10 @@
#include "third_party/re2/re2/re2.h"
#include "url/gurl.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/extensions/activity_log/uma_policy.h"
+#endif
+
namespace constants = activity_log_constants;
namespace {
@@ -329,8 +333,9 @@ ActivityLogFactory::~ActivityLogFactory() {
// Use GetInstance instead of directly creating an ActivityLog.
ActivityLog::ActivityLog(Profile* profile)
- : policy_(NULL),
- policy_type_(ActivityLogPolicy::POLICY_INVALID),
+ : database_policy_(NULL),
+ database_policy_type_(ActivityLogPolicy::POLICY_INVALID),
+ uma_policy_(NULL),
profile_(profile),
db_enabled_(false),
testing_mode_(false),
@@ -363,11 +368,21 @@ ActivityLog::ActivityLog(Profile* profile)
ExtensionSystem::Get(profile_)->ready().Post(
FROM_HERE,
base::Bind(&ActivityLog::InitInstallTracker, base::Unretained(this)));
- ChooseDefaultPolicy();
+
+// None of this should run on Android since the AL is behind ENABLE_EXTENSION
+// checks. However, UmaPolicy can't even compile on Android because it uses
+// BrowserList and related classes that aren't compiled for Android.
+#if !defined(OS_ANDROID)
+ if (!profile->IsOffTheRecord())
+ uma_policy_ = new UmaPolicy(profile_);
+#endif
+
+ ChooseDatabasePolicy();
}
-void ActivityLog::SetDefaultPolicy(ActivityLogPolicy::PolicyType policy_type) {
- if (policy_type == policy_type_)
+void ActivityLog::SetDatabasePolicy(
+ ActivityLogPolicy::PolicyType policy_type) {
+ if (database_policy_type_ == policy_type)
return;
if (!IsDatabaseEnabled() && !IsWatchdogAppActive())
return;
@@ -380,20 +395,20 @@ void ActivityLog::SetDefaultPolicy(ActivityLogPolicy::PolicyType policy_type) {
//
// However, changing policies at runtime is still not recommended, and
// likely only should be done for unit tests.
- if (policy_)
- policy_->Close();
+ if (database_policy_)
+ database_policy_->Close();
switch (policy_type) {
case ActivityLogPolicy::POLICY_FULLSTREAM:
- policy_ = new FullStreamUIPolicy(profile_);
+ database_policy_ = new FullStreamUIPolicy(profile_);
break;
case ActivityLogPolicy::POLICY_COUNTS:
- policy_ = new CountingPolicy(profile_);
+ database_policy_ = new CountingPolicy(profile_);
break;
default:
NOTREACHED();
}
- policy_type_ = policy_type;
+ database_policy_type_ = policy_type;
}
// SHUT DOWN. ------------------------------------------------------------------
@@ -403,8 +418,10 @@ void ActivityLog::Shutdown() {
}
ActivityLog::~ActivityLog() {
- if (policy_)
- policy_->Close();
+ if (uma_policy_)
+ uma_policy_->Close();
+ if (database_policy_)
+ database_policy_->Close();
}
// MAINTAIN STATUS. ------------------------------------------------------------
@@ -414,13 +431,13 @@ void ActivityLog::InitInstallTracker() {
tracker_->AddObserver(this);
}
-void ActivityLog::ChooseDefaultPolicy() {
+void ActivityLog::ChooseDatabasePolicy() {
if (!(IsDatabaseEnabled() || IsWatchdogAppActive()))
return;
if (testing_mode_)
- SetDefaultPolicy(ActivityLogPolicy::POLICY_FULLSTREAM);
+ SetDatabasePolicy(ActivityLogPolicy::POLICY_FULLSTREAM);
else
- SetDefaultPolicy(ActivityLogPolicy::POLICY_COUNTS);
+ SetDatabasePolicy(ActivityLogPolicy::POLICY_COUNTS);
}
bool ActivityLog::IsDatabaseEnabled() {
@@ -445,7 +462,7 @@ void ActivityLog::OnExtensionLoaded(const Extension* extension) {
watchdog_app_active_ = true;
profile_->GetPrefs()->SetBoolean(prefs::kWatchdogExtensionActive, true);
}
- ChooseDefaultPolicy();
+ ChooseDatabasePolicy();
}
void ActivityLog::OnExtensionUnloaded(const Extension* extension) {
@@ -462,7 +479,7 @@ void ActivityLog::OnExtensionUnloaded(const Extension* extension) {
}
void ActivityLog::OnExtensionUninstalled(const Extension* extension) {
- if (!policy_)
+ if (!database_policy_)
return;
// If the extension has been uninstalled but not disabled, we delete the
// database.
@@ -472,7 +489,7 @@ void ActivityLog::OnExtensionUninstalled(const Extension* extension) {
DeleteDatabase();
}
} else {
- policy_->RemoveExtensionData(extension->id());
+ database_policy_->RemoveExtensionData(extension->id());
}
}
@@ -503,8 +520,10 @@ void ActivityLog::LogAction(scoped_refptr<Action> action) {
// mask out incognito URLs if appropriate.
ExtractUrls(action, profile_);
- if (IsDatabaseEnabled() && policy_)
- policy_->ProcessAction(action);
+ if (uma_policy_)
+ uma_policy_->ProcessAction(action);
+ if (IsDatabaseEnabled() && database_policy_)
+ database_policy_->ProcessAction(action);
if (IsWatchdogAppActive())
observers_->Notify(&Observer::OnExtensionActivity, action);
if (testing_mode_)
@@ -568,8 +587,8 @@ void ActivityLog::GetFilteredActions(
const int daysAgo,
const base::Callback
<void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback) {
- if (policy_) {
- policy_->ReadFilteredData(
+ if (database_policy_) {
+ database_policy_->ReadFilteredData(
extension_id, type, api_name, page_url, arg_url, daysAgo, callback);
}
}
@@ -577,13 +596,13 @@ void ActivityLog::GetFilteredActions(
// DELETE ACTIONS. -------------------------------------------------------------
void ActivityLog::RemoveURLs(const std::vector<GURL>& restrict_urls) {
- if (!policy_)
+ if (!database_policy_)
return;
- policy_->RemoveURLs(restrict_urls);
+ database_policy_->RemoveURLs(restrict_urls);
}
void ActivityLog::RemoveURLs(const std::set<GURL>& restrict_urls) {
- if (!policy_)
+ if (!database_policy_)
return;
std::vector<GURL> urls;
@@ -591,7 +610,7 @@ void ActivityLog::RemoveURLs(const std::set<GURL>& restrict_urls) {
it != restrict_urls.end(); ++it) {
urls.push_back(*it);
}
- policy_->RemoveURLs(urls);
+ database_policy_->RemoveURLs(urls);
}
void ActivityLog::RemoveURL(const GURL& url) {
@@ -603,9 +622,9 @@ void ActivityLog::RemoveURL(const GURL& url) {
}
void ActivityLog::DeleteDatabase() {
- if (!policy_)
+ if (!database_policy_)
return;
- policy_->DeleteDatabase();
+ database_policy_->DeleteDatabase();
}
} // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/activity_log.h b/chrome/browser/extensions/activity_log/activity_log.h
index 1869e7f..8c93e1a 100644
--- a/chrome/browser/extensions/activity_log/activity_log.h
+++ b/chrome/browser/extensions/activity_log/activity_log.h
@@ -150,21 +150,26 @@ class ActivityLog : public BrowserContextKeyedService,
// These methods are used to choose and set the most appropriate policy.
// Changing policies at runtime is not recommended, and likely only should be
// done for unit tests.
- void ChooseDefaultPolicy();
- void SetDefaultPolicy(ActivityLogPolicy::PolicyType policy_type);
+ void ChooseDatabasePolicy();
+ void SetDatabasePolicy(ActivityLogPolicy::PolicyType policy_type);
typedef ObserverListThreadSafe<Observer> ObserverList;
scoped_refptr<ObserverList> observers_;
- // The policy object takes care of data summarization, compression, and
- // logging. The policy object is owned by the ActivityLog, but this cannot
- // be a scoped_ptr since some cleanup work must happen on the database
- // thread. Calling policy_->Close() will free the object; see the comments
- // on the ActivityDatabase class for full details.
- ActivityLogPolicy* policy_;
+ // Policy objects are owned by the ActivityLog, but cannot be scoped_ptrs
+ // since they may need to do some cleanup work on the database thread.
+ // Calling policy->Close() will free the object; see the comments on the
+ // ActivityDatabase class for full details.
- // TODO(dbabic,felt) change this into a list of policy types later.
- ActivityLogPolicy::PolicyType policy_type_;
+ // The database policy object takes care of recording & looking up data:
+ // data summarization, compression, and logging. There should only be a
+ // database_policy_ if the Watchdog app is installed or flag is set.
+ ActivityLogDatabasePolicy* database_policy_;
+ ActivityLogPolicy::PolicyType database_policy_type_;
+
+ // The UMA policy is used for recording statistics about extension behavior.
+ // This policy is always in use, except for Incognito profiles.
+ ActivityLogPolicy* uma_policy_;
Profile* profile_;
bool db_enabled_; // Whether logging to disk is currently enabled.
diff --git a/chrome/browser/extensions/activity_log/activity_log_policy.h b/chrome/browser/extensions/activity_log/activity_log_policy.h
index c97cb40..00ac27f 100644
--- a/chrome/browser/extensions/activity_log/activity_log_policy.h
+++ b/chrome/browser/extensions/activity_log/activity_log_policy.h
@@ -8,6 +8,7 @@
#include <map>
#include <set>
#include <string>
+#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -75,30 +76,6 @@ class ActivityLogPolicy {
// state to memory every 5 min.
virtual void ProcessAction(scoped_refptr<Action> action) = 0;
- // Gets all actions that match the specified fields. URLs are treated like
- // prefixes; other fields are exact matches. Empty strings are not matched to
- // anything. For the date: 0 = today, 1 = yesterday, etc.; if the data is
- // negative, it will be treated as missing.
- virtual void ReadFilteredData(
- const std::string& extension_id,
- const Action::ActionType type,
- const std::string& api_name,
- const std::string& page_url,
- const std::string& arg_url,
- const int days_ago,
- const base::Callback
- <void(scoped_ptr<Action::ActionVector>)>& callback) = 0;
-
- // Clean the relevant URL data. The cleaning may need to be different for
- // different policies. If restrict_urls is empty then all URLs are removed.
- virtual void RemoveURLs(const std::vector<GURL>& restrict_urls) = 0;
-
- // Remove all rows relating to a given extension.
- virtual void RemoveExtensionData(const std::string& extension_id) = 0;
-
- // Deletes everything in the database.
- virtual void DeleteDatabase() = 0;
-
// For unit testing only.
void SetClockForTesting(scoped_ptr<base::Clock> clock);
@@ -182,6 +159,30 @@ class ActivityLogDatabasePolicy : public ActivityLogPolicy,
// on the database thread.
virtual void Flush();
+ // Gets all actions that match the specified fields. URLs are treated like
+ // prefixes; other fields are exact matches. Empty strings are not matched to
+ // anything. For the date: 0 = today, 1 = yesterday, etc.; if the data is
+ // negative, it will be treated as missing.
+ virtual void ReadFilteredData(
+ const std::string& extension_id,
+ const Action::ActionType type,
+ const std::string& api_name,
+ const std::string& page_url,
+ const std::string& arg_url,
+ const int days_ago,
+ const base::Callback
+ <void(scoped_ptr<Action::ActionVector>)>& callback) = 0;
+
+ // Clean the relevant URL data. The cleaning may need to be different for
+ // different policies. If restrict_urls is empty then all URLs are removed.
+ virtual void RemoveURLs(const std::vector<GURL>& restrict_urls) = 0;
+
+ // Remove all rows relating to a given extension.
+ virtual void RemoveExtensionData(const std::string& extension_id) = 0;
+
+ // Deletes everything in the database.
+ virtual void DeleteDatabase() = 0;
+
protected:
// The Schedule methods dispatch the calls to the database on a
// separate thread.
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index 70e05ff..d198069 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -76,9 +76,9 @@ class ActivityLogTest : public ChromeRenderViewHostTestHarness {
void SetPolicy(bool log_arguments) {
ActivityLog* activity_log = ActivityLog::GetInstance(profile());
if (log_arguments)
- activity_log->SetDefaultPolicy(ActivityLogPolicy::POLICY_FULLSTREAM);
+ activity_log->SetDatabasePolicy(ActivityLogPolicy::POLICY_FULLSTREAM);
else
- activity_log->SetDefaultPolicy(ActivityLogPolicy::POLICY_COUNTS);
+ activity_log->SetDatabasePolicy(ActivityLogPolicy::POLICY_COUNTS);
}
bool GetDatabaseEnabled() {
diff --git a/chrome/browser/extensions/activity_log/counting_policy_unittest.cc b/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
index 6315b8d..dfd2e48 100644
--- a/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
+++ b/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
@@ -73,7 +73,7 @@ class CountingPolicyTest : public testing::Test {
// A wrapper function for CheckReadFilteredData, so that we don't need to
// enter empty string values for parameters we don't care about.
void CheckReadData(
- ActivityLogPolicy* policy,
+ ActivityLogDatabasePolicy* policy,
const std::string& extension_id,
int day,
const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
@@ -84,7 +84,7 @@ class CountingPolicyTest : public testing::Test {
// A helper function to call ReadFilteredData on a policy object and wait for
// the results to be processed.
void CheckReadFilteredData(
- ActivityLogPolicy* policy,
+ ActivityLogDatabasePolicy* policy,
const std::string& extension_id,
const Action::ActionType type,
const std::string& api_name,
@@ -303,7 +303,7 @@ class CountingPolicyTest : public testing::Test {
};
TEST_F(CountingPolicyTest, Construct) {
- ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
@@ -323,7 +323,7 @@ TEST_F(CountingPolicyTest, Construct) {
}
TEST_F(CountingPolicyTest, LogWithStrippedArguments) {
- ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
@@ -466,7 +466,7 @@ TEST_F(CountingPolicyTest, GetOlderActions) {
}
TEST_F(CountingPolicyTest, LogAndFetchFilteredActions) {
- ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
@@ -809,7 +809,7 @@ TEST_F(CountingPolicyTest, CapReturns) {
}
TEST_F(CountingPolicyTest, RemoveAllURLs) {
- ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
// Use a mock clock to ensure that events are not recorded on the wrong day
// when the test is run close to local midnight.
@@ -851,7 +851,7 @@ TEST_F(CountingPolicyTest, RemoveAllURLs) {
}
TEST_F(CountingPolicyTest, RemoveSpecificURLs) {
- ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new CountingPolicy(profile_.get());
// Use a mock clock to ensure that events are not recorded on the wrong day
// when the test is run close to local midnight.
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
index 37dec65..6f2530b 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
@@ -64,7 +64,7 @@ class FullStreamUIPolicyTest : public testing::Test {
// A wrapper function for CheckReadFilteredData, so that we don't need to
// enter empty string values for parameters we don't care about.
void CheckReadData(
- ActivityLogPolicy* policy,
+ ActivityLogDatabasePolicy* policy,
const std::string& extension_id,
int day,
const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
@@ -75,7 +75,7 @@ class FullStreamUIPolicyTest : public testing::Test {
// A helper function to call ReadFilteredData on a policy object and wait for
// the results to be processed.
void CheckReadFilteredData(
- ActivityLogPolicy* policy,
+ ActivityLogDatabasePolicy* policy,
const std::string& extension_id,
const Action::ActionType type,
const std::string& api_name,
@@ -235,7 +235,7 @@ class FullStreamUIPolicyTest : public testing::Test {
};
TEST_F(FullStreamUIPolicyTest, Construct) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
@@ -255,7 +255,7 @@ TEST_F(FullStreamUIPolicyTest, Construct) {
}
TEST_F(FullStreamUIPolicyTest, LogAndFetchActions) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
@@ -292,7 +292,7 @@ TEST_F(FullStreamUIPolicyTest, LogAndFetchActions) {
}
TEST_F(FullStreamUIPolicyTest, LogAndFetchFilteredActions) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
@@ -389,7 +389,7 @@ TEST_F(FullStreamUIPolicyTest, LogAndFetchFilteredActions) {
}
TEST_F(FullStreamUIPolicyTest, LogWithArguments) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
@@ -417,7 +417,7 @@ TEST_F(FullStreamUIPolicyTest, LogWithArguments) {
}
TEST_F(FullStreamUIPolicyTest, GetTodaysActions) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
// Use a mock clock to ensure that events are not recorded on the wrong day
// when the test is run close to local midnight. Note: Ownership is passed
@@ -465,7 +465,7 @@ TEST_F(FullStreamUIPolicyTest, GetTodaysActions) {
// Check that we can read back less recent actions in the db.
TEST_F(FullStreamUIPolicyTest, GetOlderActions) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
// Use a mock clock to ensure that events are not recorded on the wrong day
// when the test is run close to local midnight.
@@ -517,7 +517,7 @@ TEST_F(FullStreamUIPolicyTest, GetOlderActions) {
}
TEST_F(FullStreamUIPolicyTest, RemoveAllURLs) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
// Use a mock clock to ensure that events are not recorded on the wrong day
// when the test is run close to local midnight.
@@ -559,7 +559,7 @@ TEST_F(FullStreamUIPolicyTest, RemoveAllURLs) {
}
TEST_F(FullStreamUIPolicyTest, RemoveSpecificURLs) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
// Use a mock clock to ensure that events are not recorded on the wrong day
// when the test is run close to local midnight.
@@ -725,7 +725,7 @@ TEST_F(FullStreamUIPolicyTest, CapReturns) {
}
TEST_F(FullStreamUIPolicyTest, DeleteActions) {
- ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+ ActivityLogDatabasePolicy* policy = new FullStreamUIPolicy(profile_.get());
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetManifest(DictionaryBuilder()
diff --git a/chrome/browser/extensions/activity_log/uma_policy.cc b/chrome/browser/extensions/activity_log/uma_policy.cc
new file mode 100644
index 0000000..0e4e917
--- /dev/null
+++ b/chrome/browser/extensions/activity_log/uma_policy.cc
@@ -0,0 +1,363 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/activity_log/uma_policy.h"
+
+#include "base/metrics/histogram.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
+#include "chrome/browser/sessions/session_id.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/extensions/dom_action_types.h"
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+// For convenience.
+const int kNoStatus = extensions::UmaPolicy::NONE;
+const int kContentScript = 1 << extensions::UmaPolicy::CONTENT_SCRIPT;
+const int kReadDom = 1 << extensions::UmaPolicy::READ_DOM;
+const int kModifiedDom = 1 << extensions::UmaPolicy::MODIFIED_DOM;
+const int kDomMethod = 1 << extensions::UmaPolicy::DOM_METHOD;
+const int kDocumentWrite = 1 << extensions::UmaPolicy::DOCUMENT_WRITE;
+const int kInnerHtml = 1 << extensions::UmaPolicy::INNER_HTML;
+const int kCreatedScript = 1 << extensions::UmaPolicy::CREATED_SCRIPT;
+const int kCreatedIframe = 1 << extensions::UmaPolicy::CREATED_IFRAME;
+const int kCreatedDiv = 1 << extensions::UmaPolicy::CREATED_DIV;
+const int kCreatedLink = 1 << extensions::UmaPolicy::CREATED_LINK;
+const int kCreatedInput = 1 << extensions::UmaPolicy::CREATED_INPUT;
+const int kCreatedEmbed = 1 << extensions::UmaPolicy::CREATED_EMBED;
+const int kCreatedObject = 1 << extensions::UmaPolicy::CREATED_OBJECT;
+
+} // namespace
+
+namespace extensions {
+
+// Class constants, also used in testing. --------------------------------------
+
+const char UmaPolicy::kNumberOfTabs[] = "num_tabs";
+const size_t UmaPolicy::kMaxTabsTracked = 50;
+
+// Setup and shutdown. ---------------------------------------------------------
+
+UmaPolicy::UmaPolicy(Profile* profile)
+ : ActivityLogPolicy(profile), profile_(profile) {
+ DCHECK(!profile->IsOffTheRecord());
+ BrowserList::AddObserver(this);
+}
+
+UmaPolicy::~UmaPolicy() {
+ BrowserList::RemoveObserver(this);
+}
+
+// Unlike the other policies, UmaPolicy can commit suicide directly because it
+// doesn't have a dependency on a database.
+void UmaPolicy::Close() {
+ delete this;
+}
+
+// Process actions. ------------------------------------------------------------
+
+void UmaPolicy::ProcessAction(scoped_refptr<Action> action) {
+ if (!action->page_url().is_valid() && !action->arg_url().is_valid())
+ return;
+ if (action->page_incognito() || action->arg_incognito())
+ return;
+ std::string url;
+ int status = MatchActionToStatus(action);
+ if (action->page_url().is_valid()) {
+ url = CleanURL(action->page_url());
+ } else if (status & kContentScript) {
+ // This is for the tabs.executeScript case.
+ url = CleanURL(action->arg_url());
+ }
+ if (url.empty())
+ return;
+
+ SiteMap::iterator site_lookup = url_status_.find(url);
+ if (site_lookup != url_status_.end())
+ site_lookup->second[action->extension_id()] |= status;
+}
+
+int UmaPolicy::MatchActionToStatus(scoped_refptr<Action> action) {
+ if (action->action_type() == Action::ACTION_CONTENT_SCRIPT) {
+ return kContentScript;
+ } else if (action->action_type() == Action::ACTION_API_CALL &&
+ action->api_name() == "tabs.executeScript") {
+ return kContentScript;
+ } else if (action->action_type() != Action::ACTION_DOM_ACCESS) {
+ return kNoStatus;
+ }
+
+ int dom_verb;
+ if (!action->other() ||
+ !action->other()->GetIntegerWithoutPathExpansion(
+ activity_log_constants::kActionDomVerb, &dom_verb)) {
+ return kNoStatus;
+ }
+
+ int ret_bit = kNoStatus;
+ DomActionType::Type dom_type = static_cast<DomActionType::Type>(dom_verb);
+ if (dom_type == DomActionType::GETTER)
+ return kReadDom;
+ if (dom_type == DomActionType::SETTER) {
+ ret_bit |= kModifiedDom;
+ } else if (dom_type == DomActionType::METHOD) {
+ ret_bit |= kDomMethod;
+ } else {
+ return kNoStatus;
+ }
+
+ if (action->api_name() == "HTMLDocument.write" ||
+ action->api_name() == "HTMLDocument.writeln") {
+ ret_bit |= kDocumentWrite;
+ } else if (action->api_name() == "HTMLElement.innerHTML") {
+ ret_bit |= kInnerHtml;
+ } else if (action->api_name() == "Document.createElement") {
+ std::string arg;
+ action->args()->GetString(0, &arg);
+ if (arg == "script") {
+ ret_bit |= kCreatedScript;
+ } else if (arg == "iframe") {
+ ret_bit |= kCreatedIframe;
+ } else if (arg == "div") {
+ ret_bit |= kCreatedDiv;
+ } else if (arg == "a") {
+ ret_bit |= kCreatedLink;
+ } else if (arg == "input") {
+ ret_bit |= kCreatedInput;
+ } else if (arg == "embed") {
+ ret_bit |= kCreatedEmbed;
+ } else if (arg == "object") {
+ ret_bit |= kCreatedObject;
+ }
+ }
+ return ret_bit;
+}
+
+void UmaPolicy::HistogramOnClose(const std::string& url) {
+ // Let's try to avoid histogramming useless URLs.
+ if (url == "about:blank" || url.empty() || url == "chrome://newtab/")
+ return;
+
+ int statuses[MAX_STATUS-1];
+ std::memset(statuses, 0, sizeof(statuses));
+
+ SiteMap::iterator site_lookup = url_status_.find(url);
+ ExtensionMap exts = site_lookup->second;
+ ExtensionMap::iterator ext_iter;
+ for (ext_iter = exts.begin(); ext_iter != exts.end(); ++ext_iter) {
+ if (ext_iter->first == kNumberOfTabs)
+ continue;
+ for (int i = NONE + 1; i < MAX_STATUS; ++i) {
+ if (ext_iter->second & (1 << i))
+ statuses[i-1]++;
+ }
+ }
+
+ std::string prefix = "ExtensionActivity.";
+ if (GURL(url).host() != "www.google.com") {
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CONTENT_SCRIPT),
+ statuses[CONTENT_SCRIPT - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(READ_DOM),
+ statuses[READ_DOM - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(MODIFIED_DOM),
+ statuses[MODIFIED_DOM - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(DOM_METHOD),
+ statuses[DOM_METHOD - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(DOCUMENT_WRITE),
+ statuses[DOCUMENT_WRITE - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(INNER_HTML),
+ statuses[INNER_HTML - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_SCRIPT),
+ statuses[CREATED_SCRIPT - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_IFRAME),
+ statuses[CREATED_IFRAME - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_DIV),
+ statuses[CREATED_DIV - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_LINK),
+ statuses[CREATED_LINK - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_INPUT),
+ statuses[CREATED_INPUT - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_EMBED),
+ statuses[CREATED_EMBED - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_OBJECT),
+ statuses[CREATED_OBJECT - 1]);
+ } else {
+ prefix += "Google.";
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CONTENT_SCRIPT),
+ statuses[CONTENT_SCRIPT - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(READ_DOM),
+ statuses[READ_DOM - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(MODIFIED_DOM),
+ statuses[MODIFIED_DOM - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(DOM_METHOD),
+ statuses[DOM_METHOD - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(DOCUMENT_WRITE),
+ statuses[DOCUMENT_WRITE - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(INNER_HTML),
+ statuses[INNER_HTML - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_SCRIPT),
+ statuses[CREATED_SCRIPT - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_IFRAME),
+ statuses[CREATED_IFRAME - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_DIV),
+ statuses[CREATED_DIV - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_LINK),
+ statuses[CREATED_LINK - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_INPUT),
+ statuses[CREATED_INPUT - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_EMBED),
+ statuses[CREATED_EMBED - 1]);
+ UMA_HISTOGRAM_COUNTS_100(prefix + GetHistogramName(CREATED_OBJECT),
+ statuses[CREATED_OBJECT - 1]);
+ }
+}
+
+// Handle tab tracking. --------------------------------------------------------
+
+void UmaPolicy::OnBrowserAdded(Browser* browser) {
+ if (!profile_->IsSameProfile(browser->profile()))
+ return;
+ browser->tab_strip_model()->AddObserver(this);
+}
+
+void UmaPolicy::OnBrowserRemoved(Browser* browser) {
+ if (!profile_->IsSameProfile(browser->profile()))
+ return;
+ browser->tab_strip_model()->RemoveObserver(this);
+}
+
+// Use the value from SessionID::IdForTab, *not* |index|. |index| will be
+// duplicated across tabs in a session, whereas IdForTab uniquely identifies
+// each tab.
+void UmaPolicy::TabChangedAt(content::WebContents* contents,
+ int index,
+ TabChangeType change_type) {
+ if (change_type != TabStripModelObserver::LOADING_ONLY)
+ return;
+ if (!contents)
+ return;
+
+ std::string url = CleanURL(contents->GetLastCommittedURL());
+ int32 tab_id = SessionID::IdForTab(contents);
+
+ std::map<int32, std::string>::iterator tab_it = tab_list_.find(tab_id);
+
+ // Ignore tabs that haven't changed status.
+ if (tab_it != tab_list_.end() && tab_it->second == url)
+ return;
+
+ // Is this an existing tab whose URL has changed.
+ if (tab_it != tab_list_.end()) {
+ CleanupClosedPage(tab_it->second);
+ tab_list_.erase(tab_id);
+ }
+
+ // Check that tab_list_ isn't over the kMaxTabsTracked budget.
+ if (tab_list_.size() >= kMaxTabsTracked)
+ return;
+
+ // Set up the new entries.
+ tab_list_[tab_id] = url;
+ SetupOpenedPage(url);
+}
+
+// Use the value from SessionID::IdForTab, *not* |index|. |index| will be
+// duplicated across tabs in a session, whereas IdForTab uniquely identifies
+// each tab.
+void UmaPolicy::TabClosingAt(TabStripModel* tab_strip_model,
+ content::WebContents* contents,
+ int index) {
+ if (!contents)
+ return;
+ std::string url = CleanURL(contents->GetLastCommittedURL());
+ int32 tab_id = SessionID::IdForTab(contents);
+ std::map<int, std::string>::iterator tab_it = tab_list_.find(tab_id);
+ if (tab_it != tab_list_.end())
+ tab_list_.erase(tab_id);
+
+ CleanupClosedPage(url);
+}
+
+void UmaPolicy::SetupOpenedPage(const std::string& url) {
+ url_status_[url][kNumberOfTabs]++;
+}
+
+void UmaPolicy::CleanupClosedPage(const std::string& url) {
+ SiteMap::iterator old_site_lookup = url_status_.find(url);
+ if (old_site_lookup == url_status_.end())
+ return;
+ old_site_lookup->second[kNumberOfTabs]--;
+ if (old_site_lookup->second[kNumberOfTabs] == 0) {
+ HistogramOnClose(url);
+ url_status_.erase(url);
+ }
+}
+
+// Helpers. --------------------------------------------------------------------
+
+// We don't want to treat # ref navigations as if they were new pageloads.
+// So we get rid of the ref if it has it.
+// We convert to a string in the hopes that this is faster than Replacements.
+std::string UmaPolicy::CleanURL(const GURL& gurl) {
+ if (gurl.spec().empty())
+ return GURL("about:blank").spec();
+ if (!gurl.is_valid())
+ return gurl.spec();
+ if (!gurl.has_ref())
+ return gurl.spec();
+ std::string port = "";
+ if (gurl.has_port())
+ port = ":" + gurl.port();
+ std::string query = "";
+ if (gurl.has_query())
+ query = "?" + gurl.query();
+ return base::StringPrintf("%s://%s%s%s%s",
+ gurl.scheme().c_str(),
+ gurl.host().c_str(),
+ port.c_str(),
+ gurl.path().c_str(),
+ query.c_str());
+}
+
+const char* UmaPolicy::GetHistogramName(PageStatus status) {
+ switch (status) {
+ case CONTENT_SCRIPT:
+ return "ContentScript";
+ case READ_DOM:
+ return "ReadDom";
+ case MODIFIED_DOM:
+ return "ModifiedDom";
+ case DOM_METHOD:
+ return "InvokedDomMethod";
+ case DOCUMENT_WRITE:
+ return "DocumentWrite";
+ case INNER_HTML:
+ return "InnerHtml";
+ case CREATED_SCRIPT:
+ return "CreatedScript";
+ case CREATED_IFRAME:
+ return "CreatedIframe";
+ case CREATED_DIV:
+ return "CreatedDiv";
+ case CREATED_LINK:
+ return "CreatedLink";
+ case CREATED_INPUT:
+ return "CreatedInput";
+ case CREATED_EMBED:
+ return "CreatedEmbed";
+ case CREATED_OBJECT:
+ return "CreatedObject";
+ case NONE:
+ case MAX_STATUS:
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/uma_policy.h b/chrome/browser/extensions/activity_log/uma_policy.h
new file mode 100644
index 0000000..71794f2
--- /dev/null
+++ b/chrome/browser/extensions/activity_log/uma_policy.h
@@ -0,0 +1,123 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_UMA_POLICY_H_
+#define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_UMA_POLICY_H_
+
+#include <map>
+#include <string>
+
+#include "chrome/browser/extensions/activity_log/activity_log_policy.h"
+
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+// The UmaPolicy keeps track of how many extensions have read from or modified
+// a given pageload. UmaPolicy records to a histogram when a given tab is
+// closed. Caveats:
+// * If multiple tabs are open for the same URL at the same time, UmaPolicy
+// treats them as if they are the same.
+// * UmaPolicy does not record statistics for incognito tabs. (For privacy.)
+// * If the number of tabs open exceeds 50, UmaPolicy stops recording stats
+// for tabs 51+. (For memory.)
+// * UmaPolicy only handles top frames; stats are not recorded for iframes.
+class UmaPolicy : public ActivityLogPolicy,
+ public TabStripModelObserver,
+ public chrome::BrowserListObserver {
+ public:
+ // The possible status bits for a pageload. If you alter this, make sure to
+ // also update GetHistogramName.
+ enum PageStatus {
+ NONE = 0,
+ CONTENT_SCRIPT = 1,
+ READ_DOM,
+ MODIFIED_DOM,
+ DOM_METHOD,
+ DOCUMENT_WRITE,
+ INNER_HTML,
+ CREATED_SCRIPT,
+ CREATED_IFRAME,
+ CREATED_DIV,
+ CREATED_LINK,
+ CREATED_INPUT,
+ CREATED_EMBED,
+ CREATED_OBJECT,
+ MAX_STATUS // Insert new page statuses right before this one.
+ };
+
+ explicit UmaPolicy(Profile* profile);
+
+ virtual void ProcessAction(scoped_refptr<Action> action) OVERRIDE;
+ virtual void Close() OVERRIDE;
+
+ // Gets the histogram name associated with each PageStatus.
+ static const char* GetHistogramName(PageStatus status);
+
+ protected:
+ // Run when Close() is called.
+ virtual ~UmaPolicy();
+
+ private:
+ // Used as a special key in the ExtensionMap.
+ static const char kNumberOfTabs[];
+
+ // The max number of tabs we track at a time.
+ static const size_t kMaxTabsTracked;
+
+ typedef std::map<std::string, int> ExtensionMap;
+ typedef std::map<std::string, ExtensionMap> SiteMap;
+
+ // BrowserListObserver
+ virtual void OnBrowserAdded(Browser* browser) OVERRIDE;
+ virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
+
+ // TabStripModelObserver
+ // Fired when a page loads, either as a new tab or replacing the contents of
+ // an older tab.
+ virtual void TabChangedAt(content::WebContents* contents,
+ int index,
+ TabChangeType change_type) OVERRIDE;
+ // Fired when a tab closes.
+ virtual void TabClosingAt(TabStripModel* tab_strip_model,
+ content::WebContents* contents,
+ int index) OVERRIDE;
+
+ // Assign a status bitmask based on the action's properties.
+ int MatchActionToStatus(scoped_refptr<Action> action);
+
+ // When a page is opened, add it to the SiteMap url_status_.
+ void SetupOpenedPage(const std::string& url);
+
+ // When a page is closing, remove it from the SiteMap url_status_.
+ void CleanupClosedPage(const std::string& url);
+
+ // When a page is closing, save statistics about the page to histograms.
+ void HistogramOnClose(const std::string& url);
+
+ // Standardizes the way URLs are treated.
+ static std::string CleanURL(const GURL& gurl);
+
+ // Used by UmaPolicyTest.ProcessActionTest.
+ SiteMap url_status() { return url_status_; }
+
+ Profile* profile_;
+
+ // URL -> extension id -> page status.
+ SiteMap url_status_;
+
+ // tab index -> URL.
+ std::map<int32, std::string> tab_list_;
+
+ FRIEND_TEST_ALL_PREFIXES(UmaPolicyTest, CleanURLTest);
+ FRIEND_TEST_ALL_PREFIXES(UmaPolicyTest, MatchActionToStatusTest);
+ FRIEND_TEST_ALL_PREFIXES(UmaPolicyTest, ProcessActionTest);
+ FRIEND_TEST_ALL_PREFIXES(UmaPolicyTest, SiteUrlTest);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_UMA_POLICY_H_
diff --git a/chrome/browser/extensions/activity_log/uma_policy_browsertest.cc b/chrome/browser/extensions/activity_log/uma_policy_browsertest.cc
new file mode 100644
index 0000000..dd1d934
--- /dev/null
+++ b/chrome/browser/extensions/activity_log/uma_policy_browsertest.cc
@@ -0,0 +1,302 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/histogram.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/activity_log/uma_policy.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/test_extension_dir.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/common/extensions/extension_file_util.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "extensions/common/manifest_constants.h"
+#include "net/dns/mock_host_resolver.h"
+
+using extensions::UmaPolicy;
+
+const char* kGooglePrefix = "ExtensionActivity.Google";
+const char* kNonGooglePrefix = "ExtensionActivity";
+
+// These tests need to ensure that all of the extension JavaScript completes
+// before the histograms are checked. To accomplish this, the test relies on
+// some JavaScript in chrome/test/data/extensions/api_test/uma_policy/:
+// * When the test navigates to opener.com, opener.js will use window.open() to
+// pop open a new window with the appropriate URL for the test case. This
+// ensures that the testing framework never reuses a window that's still
+// running a previous test case.
+// * The test extension code in content_script.js tells background.js when it's
+// done. When it's finished, background.js closes the blocker.com window. So
+// blocker.com will remain open (and block) until the tests complete.
+class ActivityLogUmaPolicyTest : public ExtensionApiTest {
+};
+
+std::string ConcatNames(const char* prefix, int status_num) {
+ return base::StringPrintf(
+ "%s.%s",
+ prefix,
+ extensions::UmaPolicy::GetHistogramName(
+ static_cast<extensions::UmaPolicy::PageStatus>(status_num)));
+}
+
+// TODO(felt): These are disabled due to crbug.com/294500, since they fail
+// due to a blink bug. The fix went in to Blink on Thursday and should roll
+// on Monday 9/23.
+// These are all sequential navigations, so they should each be logged
+// independently.
+IN_PROC_BROWSER_TEST_F(ActivityLogUmaPolicyTest, DISABLED_SequentialNavs) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ StartEmbeddedTestServer();
+
+ const extensions::Extension* ext =
+ LoadExtension(test_data_dir_.AppendASCII("uma_policy"));
+ ASSERT_TRUE(ext);
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#google"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#google?q=a"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#google?q=b"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#cnn?q=a"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#cnn?q=b"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+
+ for (int i = UmaPolicy::NONE + 1; i < UmaPolicy::MAX_STATUS - 2; ++i) {
+ base::HistogramBase* google_histogram = base::Histogram::FactoryGet(
+ ConcatNames(kGooglePrefix, i),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> google_samples =
+ google_histogram->SnapshotSamples();
+ EXPECT_EQ(3, google_samples->TotalCount());
+ EXPECT_EQ(3, google_samples->GetCount(1));
+
+ base::HistogramBase* cnn_histogram = base::Histogram::FactoryGet(
+ ConcatNames(kNonGooglePrefix, i),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> cnn_samples =
+ cnn_histogram->SnapshotSamples();
+ if (ConcatNames(kNonGooglePrefix, i) != "ExtensionActivity.ContentScript" &&
+ ConcatNames(kNonGooglePrefix, i) != "ExtensionActivity.ReadDom") {
+ // There's a content script on opener.com that checks the location.
+ // The test is not set up to accurately record opener.com histograms due
+ // to the possibility of race conditions in the testing framework, so we
+ // can't check those values.
+ EXPECT_EQ(2, cnn_samples->GetCount(1));
+ }
+ }
+}
+
+// Two windows are open at once with the same google.com TLD.
+// However, they should be treated separately because they have different URLs.
+IN_PROC_BROWSER_TEST_F(
+ ActivityLogUmaPolicyTest, DISABLED_ParallelDistinctNavs) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ StartEmbeddedTestServer();
+
+ const extensions::Extension* ext =
+ LoadExtension(test_data_dir_.AppendASCII("uma_policy"));
+ ASSERT_TRUE(ext);
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#google?p=a"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#google?p=b"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+
+ for (int i = UmaPolicy::NONE + 1; i < UmaPolicy::MAX_STATUS - 2; ++i) {
+ base::HistogramBase* google_histogram = base::Histogram::FactoryGet(
+ ConcatNames(kGooglePrefix, i),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> google_samples =
+ google_histogram->SnapshotSamples();
+ EXPECT_EQ(2, google_samples->GetCount(1));
+ }
+}
+
+// Two windows are open at once with the same Google URLs.
+// They should be treated the same.
+IN_PROC_BROWSER_TEST_F(ActivityLogUmaPolicyTest, DISABLED_Google_ParallelSame) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ StartEmbeddedTestServer();
+
+ const extensions::Extension* ext =
+ LoadExtension(test_data_dir_.AppendASCII("uma_policy"));
+ ASSERT_TRUE(ext);
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#googlea"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#googleb"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+
+ for (int i = UmaPolicy::NONE + 1; i < UmaPolicy::MAX_STATUS - 2; ++i) {
+ base::HistogramBase* google_histogram = base::Histogram::FactoryGet(
+ ConcatNames(kGooglePrefix, i),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> google_samples =
+ google_histogram->SnapshotSamples();
+ EXPECT_EQ(1, google_samples->GetCount(1));
+ }
+}
+
+// Two windows are open at once with the same non-Google URLs.
+// They should be treated the same.
+IN_PROC_BROWSER_TEST_F(ActivityLogUmaPolicyTest,
+ DISABLED_NonGoogle_ParallelSame) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ StartEmbeddedTestServer();
+
+ const extensions::Extension* ext =
+ LoadExtension(test_data_dir_.AppendASCII("uma_policy"));
+ ASSERT_TRUE(ext);
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#cnna"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#cnnb"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+
+ for (int i = UmaPolicy::NONE + 1; i < UmaPolicy::MAX_STATUS - 2; ++i) {
+ base::HistogramBase* cnn_histogram = base::Histogram::FactoryGet(
+ ConcatNames(kNonGooglePrefix, i),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> cnn_samples =
+ cnn_histogram->SnapshotSamples();
+ if (ConcatNames(kNonGooglePrefix, i) != "ExtensionActivity.ContentScript" &&
+ ConcatNames(kNonGooglePrefix, i) != "ExtensionActivity.ReadDom") {
+ // There's a content script on opener.com that checks the location.
+ // The test is not set up to accurately record opener.com histograms due
+ // to the possibility of race conditions in the testing framework, so we
+ // can't check those values.
+ EXPECT_EQ(1, cnn_samples->GetCount(1));
+ }
+ }
+}
+
+// This runs with multiple extensions installed.
+IN_PROC_BROWSER_TEST_F(ActivityLogUmaPolicyTest, DISABLED_MultipleExtensions) {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ StartEmbeddedTestServer();
+
+ const extensions::Extension* ext =
+ LoadExtension(test_data_dir_.AppendASCII("uma_policy"));
+ ASSERT_TRUE(ext);
+
+ const char* script2 =
+ "document.createElement('script');"
+ "document.createElement('iframe');"
+ "document.createElement('div');"
+ "document.createElement('embed');"
+ "document.createElement('object');";
+
+ const char* manifest =
+ "{"
+ " \"name\": \"Activity Log UMA Policy Test Extension\","
+ " \"version\": \"0.%s\","
+ " \"description\": \"Testing the histogramming\","
+ " \"content_scripts\": ["
+ " {"
+ " \"matches\": "
+ " [\"http://www.google.com/*\","
+ " \"http://www.cnn.com/*\"],"
+ " \"js\": [\"content_script.js\"]"
+ " }"
+ " ],"
+ " \"manifest_version\": 2"
+ "}";
+
+ extensions::TestExtensionDir dir2;
+ dir2.WriteManifest(base::StringPrintf(manifest, "2"));
+ dir2.WriteFile(FILE_PATH_LITERAL("content_script.js"), script2);
+ LoadExtension(dir2.unpacked_path());
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#google"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("http://www.opener.com/#cnn?q=b"), NEW_WINDOW,
+ ui_test_utils::BROWSER_TEST_NONE);
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+ browser(), GURL("http://www.blocker.com"), 2);
+
+ const char* subset_one[] = {
+ "CreatedLink",
+ "InnerHtml",
+ "DocumentWrite"
+ };
+
+ const char* subset_two[] = {
+ "ContentScript",
+ "CreatedScript",
+ "CreatedIframe",
+ "CreatedDiv",
+ "CreatedEmbed",
+ "CreatedObject",
+ "InvokedDomMethod"
+ };
+
+ // These were only touched by one of the scripts.
+ for (size_t i = 0; i < arraysize(subset_one); ++i) {
+ base::HistogramBase* google_histogram = base::Histogram::FactoryGet(
+ std::string(kGooglePrefix) + "." + std::string(subset_one[i]),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> google_samples =
+ google_histogram->SnapshotSamples();
+ EXPECT_EQ(1, google_samples->GetCount(1));
+
+ base::HistogramBase* cnn_histogram = base::Histogram::FactoryGet(
+ std::string(kNonGooglePrefix) + "." + std::string(subset_one[i]),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> cnn_samples =
+ cnn_histogram->SnapshotSamples();
+ EXPECT_EQ(1, cnn_samples->GetCount(1));
+ }
+
+ // These were touched by both scripts.
+ for (size_t i = 0; i < arraysize(subset_two); ++i) {
+ base::HistogramBase* google_histogram = base::Histogram::FactoryGet(
+ std::string(kGooglePrefix) + "." + std::string(subset_two[i]),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> google_samples =
+ google_histogram->SnapshotSamples();
+ EXPECT_EQ(1, google_samples->GetCount(2));
+
+ base::HistogramBase* cnn_histogram = base::Histogram::FactoryGet(
+ std::string(kNonGooglePrefix) + "." + std::string(subset_two[i]),
+ 1, 100, 50, base::HistogramBase::kNoFlags);
+ scoped_ptr<base::HistogramSamples> cnn_samples =
+ cnn_histogram->SnapshotSamples();
+ EXPECT_EQ(1, cnn_samples->GetCount(2));
+ }
+
+}
diff --git a/chrome/browser/extensions/activity_log/uma_policy_unittest.cc b/chrome/browser/extensions/activity_log/uma_policy_unittest.cc
new file mode 100644
index 0000000..3fb421a
--- /dev/null
+++ b/chrome/browser/extensions/activity_log/uma_policy_unittest.cc
@@ -0,0 +1,225 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/time/time.h"
+#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
+#include "chrome/browser/extensions/activity_log/uma_policy.h"
+#include "chrome/common/extensions/dom_action_types.h"
+#include "chrome/test/base/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+class UmaPolicyTest : public testing::Test {
+ public:
+ UmaPolicyTest() {
+ profile_.reset(new TestingProfile());
+ }
+
+ protected:
+ scoped_ptr<TestingProfile> profile_;
+};
+
+TEST_F(UmaPolicyTest, Construct) {
+ ActivityLogPolicy* policy = new UmaPolicy(profile_.get());
+ policy->Close();
+}
+
+TEST_F(UmaPolicyTest, MatchActionToStatusTest) {
+ UmaPolicy* policy = new UmaPolicy(profile_.get());
+
+ scoped_refptr<Action> action = new Action(
+ "id", base::Time::Now(), Action::ACTION_API_CALL, "extension.connect");
+ ASSERT_EQ(UmaPolicy::NONE, policy->MatchActionToStatus(action));
+
+ action = new Action(
+ "id", base::Time::Now(), Action::ACTION_API_CALL, "tabs.executeScript");
+ ASSERT_EQ(
+ (1 << UmaPolicy::CONTENT_SCRIPT), policy->MatchActionToStatus(action));
+
+ action = new Action(
+ "id", base::Time::Now(), Action::ACTION_CONTENT_SCRIPT, "");
+ ASSERT_TRUE(
+ (1 << UmaPolicy::CONTENT_SCRIPT) & policy->MatchActionToStatus(action));
+ ASSERT_EQ(
+ (1 << UmaPolicy::CONTENT_SCRIPT), policy->MatchActionToStatus(action));
+
+ action = new Action(
+ "id", base::Time::Now(), Action::ACTION_DOM_ACCESS, "Document.location");
+ action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+ DomActionType::GETTER);
+ ASSERT_TRUE((1 << UmaPolicy::READ_DOM) & policy->MatchActionToStatus(action));
+ ASSERT_EQ((1 << UmaPolicy::READ_DOM), policy->MatchActionToStatus(action));
+
+ action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+ DomActionType::SETTER);
+ ASSERT_TRUE(
+ (1 << UmaPolicy::MODIFIED_DOM) & policy->MatchActionToStatus(action));
+ ASSERT_EQ(
+ (1 << UmaPolicy::MODIFIED_DOM), policy->MatchActionToStatus(action));
+
+ action = new Action(
+ "id", base::Time::Now(), Action::ACTION_DOM_ACCESS, "HTMLDocument.write");
+ action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+ DomActionType::METHOD);
+ ASSERT_TRUE(
+ (1 << UmaPolicy::DOCUMENT_WRITE) & policy->MatchActionToStatus(action));
+ ASSERT_TRUE(
+ (1 << UmaPolicy::DOM_METHOD) & policy->MatchActionToStatus(action));
+
+ action = new Action("id",
+ base::Time::Now(),
+ Action::ACTION_DOM_ACCESS,
+ "Document.createElement");
+ scoped_ptr<base::ListValue> args(new base::ListValue());
+ args->Set(0, new base::StringValue("script"));
+ action->set_args(args.Pass());
+ ASSERT_EQ(UmaPolicy::NONE, policy->MatchActionToStatus(action));
+ action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+ DomActionType::METHOD);
+ ASSERT_TRUE(
+ (1 << UmaPolicy::CREATED_SCRIPT) & policy->MatchActionToStatus(action));
+ ASSERT_TRUE(
+ (1 << UmaPolicy::DOM_METHOD) & policy->MatchActionToStatus(action));
+
+ policy->Close();
+}
+
+TEST_F(UmaPolicyTest, SiteUrlTest) {
+ UmaPolicy* policy = new UmaPolicy(profile_.get());
+
+ const std::string site0 = "http://www.zzz.com";
+ const std::string site1 = "http://www.foo.com";
+ const std::string site2 = "http://www.google.com#a";
+ const std::string site3 = "http://www.google.com#bb";
+
+ // Record some opened sites.
+ policy->SetupOpenedPage(site1);
+ policy->SetupOpenedPage(site2);
+ policy->SetupOpenedPage(site2);
+ policy->SetupOpenedPage(site3);
+ policy->SetupOpenedPage(site3);
+ policy->SetupOpenedPage(site3);
+
+ // Check that site1, site2, and site3 were recorded.
+ ASSERT_EQ(3u, policy->url_status().size());
+ ASSERT_EQ(1, policy->url_status()[site1][UmaPolicy::kNumberOfTabs]);
+ ASSERT_EQ(2, policy->url_status()[site2][UmaPolicy::kNumberOfTabs]);
+ ASSERT_EQ(3, policy->url_status()[site3][UmaPolicy::kNumberOfTabs]);
+
+ // Remove some sites.
+ policy->CleanupClosedPage(site0);
+ policy->CleanupClosedPage(site2);
+ policy->CleanupClosedPage(site2);
+ policy->CleanupClosedPage(site3);
+
+ // Check that the removal worked.
+ ASSERT_EQ(2u, policy->url_status().size());
+ ASSERT_EQ(1, policy->url_status()[site1][UmaPolicy::kNumberOfTabs]);
+ ASSERT_EQ(2, policy->url_status()[site3][UmaPolicy::kNumberOfTabs]);
+
+ policy->Close();
+}
+
+TEST_F(UmaPolicyTest, ProcessActionTest) {
+ const std::string site_a = "http://www.zzz.com/";
+ const std::string site_b = "http://www.foo.com/";
+ const std::string ext_a = "a";
+ const std::string ext_b = "b";
+ UmaPolicy* policy = new UmaPolicy(profile_.get());
+
+ // Populate with a few different pages.
+ policy->SetupOpenedPage(site_a);
+ policy->SetupOpenedPage(site_b);
+
+ // Process a few actions for site_a.
+ scoped_refptr<Action> action1 = new Action(
+ ext_a, base::Time::Now(), Action::ACTION_CONTENT_SCRIPT, "");
+ action1->set_page_url(GURL(site_a));
+ policy->ProcessAction(action1);
+
+ scoped_refptr<Action> action2 = new Action(
+ ext_a, base::Time::Now(), Action::ACTION_CONTENT_SCRIPT, "");
+ action2->set_page_url(GURL(site_a));
+ policy->ProcessAction(action2);
+
+ scoped_refptr<Action> action3 = new Action(
+ ext_b, base::Time::Now(), Action::ACTION_DOM_ACCESS, "Document.location");
+ action3->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+ DomActionType::GETTER);
+ action3->set_page_url(GURL(site_a));
+ policy->ProcessAction(action3);
+
+ // Process an action for site_b.
+ scoped_refptr<Action> action4 = new Action(
+ ext_a, base::Time::Now(), Action::ACTION_DOM_ACCESS, "Document.location");
+ action4->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+ DomActionType::SETTER);
+ action4->set_page_url(GURL(site_b));
+ policy->ProcessAction(action4);
+
+ scoped_refptr<Action> action5 = new Action(
+ ext_b, base::Time::Now(), Action::ACTION_DOM_ACCESS, "Document.location");
+ action5->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
+ DomActionType::SETTER);
+ action5->set_page_url(GURL(site_b));
+ policy->ProcessAction(action5);
+
+ scoped_refptr<Action> action6 = new Action(
+ ext_b, base::Time::Now(), Action::ACTION_API_CALL, "tabs.executeScript");
+ action6->set_arg_url(GURL(site_b));
+ policy->ProcessAction(action6);
+
+ // Now check what's been recorded.
+ ASSERT_EQ(2u, policy->url_status().size());
+
+ ASSERT_EQ(3u, policy->url_status()[site_a].size());
+ ASSERT_TRUE(
+ (1 << UmaPolicy::CONTENT_SCRIPT) & policy->url_status()[site_a][ext_a]);
+ ASSERT_FALSE(
+ (1 << UmaPolicy::CONTENT_SCRIPT) & policy->url_status()[site_a][ext_b]);
+ ASSERT_TRUE((1 << UmaPolicy::READ_DOM) & policy->url_status()[site_a][ext_b]);
+ ASSERT_FALSE(
+ (1 << UmaPolicy::READ_DOM) & policy->url_status()[site_a][ext_a]);
+
+ ASSERT_EQ(3u, policy->url_status()[site_b].size());
+ ASSERT_TRUE(
+ (1 << UmaPolicy::MODIFIED_DOM) & policy->url_status()[site_b][ext_a]);
+ ASSERT_TRUE(
+ (1 << UmaPolicy::MODIFIED_DOM) & policy->url_status()[site_b][ext_b]);
+ ASSERT_TRUE(
+ (1 << UmaPolicy::CONTENT_SCRIPT) & policy->url_status()[site_b][ext_b]);
+
+ policy->Close();
+}
+
+TEST_F(UmaPolicyTest, CleanURLTest) {
+ ASSERT_EQ("http://www.google.com/",
+ UmaPolicy::CleanURL(GURL("http://www.google.com/")));
+ ASSERT_EQ("http://www.google.com/",
+ UmaPolicy::CleanURL(GURL("http://www.google.com")));
+ ASSERT_EQ("http://www.google.com:8080/a.html",
+ UmaPolicy::CleanURL(GURL("http://www.google.com:8080/a.html")));
+ ASSERT_EQ("http://www.google.com/",
+ UmaPolicy::CleanURL(GURL("http://www.google.com/#a")));
+ ASSERT_EQ("http://www.google.com/",
+ UmaPolicy::CleanURL(GURL("http://www.google.com/#aaaa")));
+ ASSERT_EQ("http://www.google.com/?q=a",
+ UmaPolicy::CleanURL(GURL("http://www.google.com/?q=a")));
+
+ ASSERT_EQ("http://www.cnn.com/",
+ UmaPolicy::CleanURL(GURL("http://www.cnn.com/")));
+ ASSERT_EQ("http://www.cnn.com:8080/a.html",
+ UmaPolicy::CleanURL(GURL("http://www.cnn.com:8080/a.html")));
+ ASSERT_EQ("http://www.cnn.com/",
+ UmaPolicy::CleanURL(GURL("http://www.cnn.com")));
+ ASSERT_EQ("http://www.cnn.com/",
+ UmaPolicy::CleanURL(GURL("http://www.cnn.com/#a")));
+ ASSERT_EQ("http://www.cnn.com/",
+ UmaPolicy::CleanURL(GURL("http://www.cnn.com/#aaaa")));
+ ASSERT_EQ("http://www.cnn.com/?q=a",
+ UmaPolicy::CleanURL(GURL("http://www.cnn.com/?q=a")));
+}
+
+} // namespace extensions
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 706ecaaa..404c1e1 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -91,6 +91,8 @@
'browser/extensions/activity_log/database_string_table.h',
'browser/extensions/activity_log/fullstream_ui_policy.cc',
'browser/extensions/activity_log/fullstream_ui_policy.h',
+ 'browser/extensions/activity_log/uma_policy.cc',
+ 'browser/extensions/activity_log/uma_policy.h',
'browser/extensions/activity_log/web_request_constants.cc',
'browser/extensions/activity_log/web_request_constants.h',
'browser/extensions/admin_policy.cc',
@@ -1046,6 +1048,7 @@
'sources!': [
'browser/apps/shortcut_manager.cc',
'browser/apps/shortcut_manager_factory.cc',
+ 'browser/extensions/activity_log/uma_policy.cc',
'browser/extensions/api/declarative/rules_registry_with_cache.cc',
'browser/extensions/api/processes/processes_api_constants.cc',
'browser/extensions/api/web_navigation/frame_navigation_state.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 06d5696..81e86a3 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1007,6 +1007,7 @@
'browser/errorpage_browsertest.cc',
'browser/extensions/active_tab_apitest.cc',
'browser/extensions/activity_log/activity_log_browsertest.cc',
+ 'browser/extensions/activity_log/uma_policy_browsertest.cc',
'browser/extensions/alert_apitest.cc',
'browser/extensions/all_urls_apitest.cc',
'browser/extensions/api/activity_log_private/activity_log_private_apitest.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 5b2f89e..23fc8c7 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -775,6 +775,7 @@
'browser/extensions/activity_log/counting_policy_unittest.cc',
'browser/extensions/activity_log/database_string_table_unittest.cc',
'browser/extensions/activity_log/fullstream_ui_policy_unittest.cc',
+ 'browser/extensions/activity_log/uma_policy_unittest.cc',
'browser/extensions/admin_policy_unittest.cc',
'browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc',
'browser/extensions/api/alarms/alarms_api_unittest.cc',
diff --git a/chrome/test/data/extensions/api_test/uma_policy/background.js b/chrome/test/data/extensions/api_test/uma_policy/background.js
new file mode 100644
index 0000000..cca513c
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/uma_policy/background.js
@@ -0,0 +1,34 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var done = false;
+var first = false;
+var allComplete = false;
+
+chrome.runtime.onMessage.addListener(
+ function(request, sender, sendResponse) {
+ if (request['testType'] == 'single') {
+ done = true;
+ } else if (request['testType'] == 'double') {
+ if (first)
+ done = true;
+ else
+ first = true;
+ }
+ if (done) {
+ chrome.tabs.query({url: 'http://www.blocker.com/'}, function(tabs) {
+ chrome.tabs.update(tabs[0]['id'], {url: 'http://www.done.com'});
+ });
+ allComplete = true;
+ }
+ });
+
+chrome.tabs.onUpdated.addListener(
+ function(tabid, changeinfo, tab) {
+ if (done && !allComplete) {
+ if (changeinfo.url == 'http://www.blocker.com/') {
+ chrome.tabs.update(tabid, {url: 'http://www.done.com'});
+ }
+ }
+ });
diff --git a/chrome/test/data/extensions/api_test/uma_policy/content_script.js b/chrome/test/data/extensions/api_test/uma_policy/content_script.js
new file mode 100644
index 0000000..64dde84
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/uma_policy/content_script.js
@@ -0,0 +1,38 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+document.write('<div id="hi">hello</div>');
+
+document.getElementById('hi').innerHTML = 'innerhtml';
+
+document.createElement('script');
+document.createElement('iframe');
+document.createElement('div');
+document.createElement('a');
+document.createElement('input');
+document.createElement('embed');
+document.createElement('object');
+
+var endConditions = {};
+endConditions['http://www.google.com/'] = true;
+endConditions['http://www.google.com/?q=a'] = true;
+endConditions['http://www.google.com/?q=b'] = true;
+endConditions['http://www.cnn.com/?q=a'] = true;
+endConditions['http://www.cnn.com/?q=b'] = true;
+
+var doubleEndConditions = {};
+doubleEndConditions['http://www.google.com/?p=a'] = true;
+doubleEndConditions['http://www.google.com/?p=b'] = true;
+doubleEndConditions['http://www.cnn.com/#a'] = true;
+doubleEndConditions['http://www.cnn.com/#b'] = true;
+doubleEndConditions['http://www.google.com/#a'] = true;
+doubleEndConditions['http://www.google.com/#b'] = true;
+
+if (document.location in endConditions) {
+ chrome.runtime.sendMessage({testType: 'single'}, function(response) {});
+} else if (document.location in doubleEndConditions) {
+ chrome.runtime.sendMessage({testType: 'double'}, function(response) {});
+}
+
+window.close();
diff --git a/chrome/test/data/extensions/api_test/uma_policy/manifest.json b/chrome/test/data/extensions/api_test/uma_policy/manifest.json
new file mode 100644
index 0000000..23fecd1
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/uma_policy/manifest.json
@@ -0,0 +1,20 @@
+{
+ "name": "Activity Log UMA Policy Test Extension",
+ "version": "0.1",
+ "description": "Testing the histogramming",
+ "content_scripts": [
+ {
+ "matches": ["http://www.google.com/*", "http://www.cnn.com/*"],
+ "js": ["content_script.js"]
+ },
+ {
+ "matches": ["http://www.opener.com/*"],
+ "js": ["opener.js"]
+ }
+ ],
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": [ "http://www.done.com/*", "tabs" , "http://www.blocker.com/*"],
+ "manifest_version": 2
+}
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 88c2894..34c5bc2 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3667,6 +3667,183 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="ExtensionActivity.ContentScript">
+ <summary>
+ For each pageload, the number of extensions that inject a content script.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.CreatedDiv">
+ <summary>
+ For each pageload, the number of extensions that create divs to add to the
+ page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.CreatedEmbed">
+ <summary>
+ For each pageload, the number of extensions that create 'embed' elements to
+ add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.CreatedIframe">
+ <summary>
+ For each pageload, the number of extensions that create iframes to add to
+ the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.CreatedInput">
+ <summary>
+ For each pageload, the number of extensions that create inputs to add to the
+ page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.CreatedLink">
+ <summary>
+ For each pageload, the number of extensions that create links to add to the
+ page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.CreatedObject">
+ <summary>
+ For each pageload, the number of extensions that create 'object' elements to
+ add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.CreatedScript">
+ <summary>
+ For each pageload, the number of extensions that create script tags to add
+ to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.DocumentWrite">
+ <summary>
+ For each pageload, the number of extensions that use document.write.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.ContentScript">
+ <summary>
+ For each www.google.com pageload, the number of extensions that inject a
+ content script.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.CreatedDiv">
+ <summary>
+ For each www.google.com pageload, the number of extensions that create divs
+ to add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.CreatedEmbed">
+ <summary>
+ For each www.google.com pageload, the number of extensions that create
+ 'embed' elements to add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.CreatedIframe">
+ <summary>
+ For each www.google.com pageload, the number of extensions that create
+ iframes to add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.CreatedInput">
+ <summary>
+ For each www.google.com pageload, the number of extensions that create
+ inputs to add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.CreatedLink">
+ <summary>
+ For each www.google.com pageload, the number of extensions that create links
+ to add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.CreatedObject">
+ <summary>
+ For each www.google.com pageload, the number of extensions that create
+ 'object' elements to add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.CreatedScript">
+ <summary>
+ For each www.google.com pageload, the number of extensions that create
+ script tags to add to the page.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.DocumentWrite">
+ <summary>
+ For each www.google.com pageload, the number of extensions that use
+ document.write.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.InnerHtml">
+ <summary>
+ For each www.google.com pageload, the number of extensions that set
+ innerHTML.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.InvokedDomMethod">
+ <summary>
+ For each www.google.com pageload, the number of extensions that invoke DOM
+ methods.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.ModifiedDom">
+ <summary>
+ For each www.google.com pageload, the number of extensions that set the
+ value of DOM properties via assignments.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.Google.ReadDom">
+ <summary>
+ For each www.google.com pageload, the number of extensions that read from
+ the DOM.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.InnerHtml">
+ <summary>
+ For each pageload, the number of extensions that set innerHTML.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.InvokedDomMethod">
+ <summary>
+ For each pageload, the number of extensions that invoke DOM methods.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.ModifiedDom">
+ <summary>
+ For each pageload, the number of extensions that set the value of DOM
+ properties via assignments.
+ </summary>
+</histogram>
+
+<histogram name="ExtensionActivity.ReadDom">
+ <summary>
+ For each pageload, the number of extensions that read from the DOM.
+ </summary>
+</histogram>
+
<histogram name="ExtensionBlacklist.BlacklistInstalled"
enum="ExtensionLocation">
<summary>