Commit a82448e8 authored by Josh Karlin's avatar Josh Karlin Committed by Commit Bot

Add UKM to measure time on popup's webcontents

Add UKM for time before a popup is closed and whether it was closed due to
user gesture or not.

Bug: 825875
Change-Id: I9cd9abf40ee11c8404c7629b62cbdadc6b4069c9
Reviewed-on: https://chromium-review.googlesource.com/1005317
Commit-Queue: Josh Karlin <jkarlin@chromium.org>
Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551342}
parent 95fe44b3
......@@ -8,8 +8,12 @@
#include "base/metrics/histogram_macros.h"
#include "base/time/default_tick_clock.h"
#include "chrome/browser/ui/blocked_content/popup_opener_tab_helper.h"
#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
DEFINE_WEB_CONTENTS_USER_DATA_KEY(PopupTracker);
......@@ -30,7 +34,8 @@ PopupTracker::PopupTracker(content::WebContents* contents,
: content::WebContentsObserver(contents),
visibility_tracker_(
base::DefaultTickClock::GetInstance(),
contents->GetVisibility() != content::Visibility::HIDDEN) {
contents->GetVisibility() != content::Visibility::HIDDEN),
opener_source_id_(ukm::GetSourceIdForWebContentsDocument(opener)) {
if (auto* popup_opener = PopupOpenerTabHelper::FromWebContents(opener))
popup_opener->OnOpenedPopup(this);
}
......@@ -56,6 +61,14 @@ void PopupTracker::WebContentsDestroyed() {
total_foreground_duration, base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromHours(6), 50);
}
if (opener_source_id_ != ukm::kInvalidSourceId) {
ukm::builders::Popup_Closed(opener_source_id_)
.SetEngagementTime(ukm::GetExponentialBucketMinForUserTiming(
total_foreground_duration.InMilliseconds()))
.SetUserInitiatedClose(web_contents()->GetClosedByUserGesture())
.Record(ukm::UkmRecorder::Get());
}
}
void PopupTracker::DidFinishNavigation(
......
......@@ -11,6 +11,7 @@
#include "chrome/browser/ui/blocked_content/scoped_visibility_tracker.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
namespace content {
class WebContents;
......@@ -49,6 +50,10 @@ class PopupTracker : public content::WebContentsObserver,
ScopedVisibilityTracker visibility_tracker_;
// The id of the web contents that created the popup at the time of creation.
// SourceIds are permanent so it's okay to use at any point so long as it's
// not invalid.
const ukm::SourceId opener_source_id_;
DISALLOW_COPY_AND_ASSIGN(PopupTracker);
};
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/ui/blocked_content/popup_tracker.h"
#include <memory>
#include <string>
#include "base/supports_user_data.h"
......@@ -18,23 +19,32 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/window_open_disposition.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace {
const char kPopupFirstDocumentEngagement[] =
"ContentSettings.Popups.FirstDocumentEngagementTime2";
const char kPopupEngagement[] = "ContentSettings.Popups.EngagementTime";
const char kPopupGestureClose[] =
"ContentSettings.Popups.EngagementTime.GestureClose";
const char kUkmEngagementTime[] = "EngagementTime";
const char kUkmUserInitiatedClose[] = "UserInitiatedClose";
} // namespace
using UkmEntry = ukm::builders::Popup_Closed;
class PopupTrackerBrowserTest : public InProcessBrowserTest {
public:
PopupTrackerBrowserTest() {}
......@@ -43,6 +53,33 @@ class PopupTrackerBrowserTest : public InProcessBrowserTest {
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
}
void PreRunTestOnMainThread() override {
InProcessBrowserTest::PreRunTestOnMainThread();
test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
protected:
enum UserClosedPopup { kTrue, kFalse };
bool GetNumPopupUkmEntries() {
return test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName).size();
}
void VerifyUkm(const GURL& expected_url,
UserClosedPopup expected_user_closed) {
const auto& entries =
test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName);
ASSERT_EQ(1u, entries.size());
const auto* entry = entries[0];
test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, expected_url);
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(entry, kUkmEngagementTime));
test_ukm_recorder_->ExpectEntryMetric(
entry, kUkmUserInitiatedClose,
expected_user_closed == UserClosedPopup::kTrue ? 1u : 0u);
}
std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
};
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, NoPopup_NoTracker) {
......@@ -55,13 +92,15 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, NoPopup_NoTracker) {
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 0);
tester.ExpectTotalCount(kPopupEngagement, 0);
tester.ExpectTotalCount(kPopupGestureClose, 0);
EXPECT_EQ(0u, GetNumPopupUkmEntries());
}
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest,
WindowOpenPopup_HasTracker_GestureClose) {
base::HistogramTester tester;
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/title1.html"));
const GURL first_url = embedded_test_server()->GetURL("/title1.html");
ui_test_utils::NavigateToURL(browser(), first_url);
content::TestNavigationObserver navigation_observer(nullptr, 1);
navigation_observer.StartWatchingNewWebContents();
......@@ -86,15 +125,17 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest,
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1);
tester.ExpectTotalCount(kPopupGestureClose, 1);
VerifyUkm(first_url, UserClosedPopup::kTrue);
}
// OpenURLFromTab goes through a different code path than traditional popups
// that use window.open(). Make sure the tracker is created in those cases.
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ControlClick_HasTracker) {
base::HistogramTester tester;
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/popup_blocker/popup-simulated-click-on-anchor.html"));
const GURL url = embedded_test_server()->GetURL(
"/popup_blocker/popup-simulated-click-on-anchor.html");
ui_test_utils::NavigateToURL(browser(), url);
// Mac uses command instead of control for the new tab action.
bool is_mac = false;
......@@ -124,13 +165,15 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ControlClick_HasTracker) {
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1);
VerifyUkm(url, UserClosedPopup::kFalse);
}
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ShiftClick_HasTracker) {
base::HistogramTester tester;
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/popup_blocker/popup-simulated-click-on-anchor.html"));
const GURL url = embedded_test_server()->GetURL(
"/popup_blocker/popup-simulated-click-on-anchor.html");
ui_test_utils::NavigateToURL(browser(), url);
content::TestNavigationObserver navigation_observer(nullptr, 1);
navigation_observer.StartWatchingNewWebContents();
......@@ -156,17 +199,19 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ShiftClick_HasTracker) {
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1);
VerifyUkm(url, UserClosedPopup::kFalse);
}
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, WhitelistedPopup_HasTracker) {
base::HistogramTester tester;
const GURL url =
embedded_test_server()->GetURL("/popup_blocker/popup-window-open.html");
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Is blocked by the popup blocker.
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/popup_blocker/popup-window-open.html"));
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
......@@ -187,6 +232,8 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, WhitelistedPopup_HasTracker) {
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1);
VerifyUkm(url, UserClosedPopup::kFalse);
}
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, NoOpener_NoTracker) {
......@@ -199,4 +246,5 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, NoOpener_NoTracker) {
browser()->tab_strip_model()->GetWebContentsAt(1);
EXPECT_FALSE(PopupTracker::FromWebContents(new_contents));
EXPECT_EQ(0u, GetNumPopupUkmEntries());
}
......@@ -22,6 +22,10 @@ int64_t GetExponentialBucketMin(int64_t sample, double bucket_spacing) {
bucket_spacing, std::floor(std::log(sample) / std::log(bucket_spacing))));
}
int64_t GetExponentialBucketMinForUserTiming(int64_t sample) {
return GetExponentialBucketMin(sample, 2.0);
}
int64_t GetLinearBucketMin(int64_t sample, int32_t bucket_size) {
DCHECK(bucket_size > 0);
// Round down to the nearest multiple of |bucket_size| (for negative samples,
......
......@@ -17,6 +17,10 @@ namespace ukm {
int64_t METRICS_EXPORT GetExponentialBucketMin(int64_t sample,
double bucket_spacing);
// Like GetExponentialBucketMin but uses a standard bucket_spacing of 2.0 for
// timing user actions.
int64_t METRICS_EXPORT GetExponentialBucketMinForUserTiming(int64_t sample);
// Calculates the linear bucket |sample| falls in and returns the lower
// threshold of that bucket (i.e., rounding down to the nearest multiple of
// |bucket_size|). Negative sample values will be rounded down as well (away
......
......@@ -2567,6 +2567,26 @@ be describing additional metrics about the same event.
</summary>
</event>
<event name="Popup.Closed">
<owner>csharrison@chromium.org</owner>
<owner>jkarlin@chromium.org</owner>
<summary>
A popup window was closed.
</summary>
<metric name="EngagementTime">
<summary>
The time (in ms, rounded down to the nearest power of 2) a popup
WebContents is visible / foregrounded, until it is closed. Keyed by the
popup opener's URL (not the URL of the popup).
</summary>
</metric>
<metric name="UserInitiatedClose">
<summary>
Boolean value to represent whether the popup was closed by user gesture.
</summary>
</metric>
</event>
<event name="ScreenBrightness">
<owner>pdyson@chromium.org</owner>
<summary>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment