Commit 6c8b7a36 authored by Charlie Harrison's avatar Charlie Harrison Committed by Commit Bot

Add some additional metadata to Popup.Closed UKM

This CL extends the amount of useful information a popup tracker
has, and reports it to UKM on close.

This data will additionally be useful if we add spammy popup detection
+ enforcement at run time.

Bug: 825875
Change-Id: I1366b795c500cbf58730887cf4e71c3f32b945a8
Reviewed-on: https://chromium-review.googlesource.com/c/1313508Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Commit-Queue: Charlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607725}
parent 9196ef69
...@@ -137,8 +137,9 @@ void PopupBlockerTabHelper::ShowBlockedPopup( ...@@ -137,8 +137,9 @@ void PopupBlockerTabHelper::ShowBlockedPopup(
Navigate(&popup->params); Navigate(&popup->params);
#endif #endif
if (popup->params.navigated_or_inserted_contents) { if (popup->params.navigated_or_inserted_contents) {
PopupTracker::CreateForWebContents( auto* tracker = PopupTracker::CreateForWebContents(
popup->params.navigated_or_inserted_contents, web_contents()); popup->params.navigated_or_inserted_contents, web_contents());
tracker->set_is_trusted(true);
if (popup->params.disposition == WindowOpenDisposition::NEW_POPUP) { if (popup->params.disposition == WindowOpenDisposition::NEW_POPUP) {
content::RenderFrameHost* host = content::RenderFrameHost* host =
......
...@@ -15,14 +15,16 @@ ...@@ -15,14 +15,16 @@
#include "services/metrics/public/cpp/ukm_builders.h" #include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h" #include "services/metrics/public/cpp/ukm_recorder.h"
void PopupTracker::CreateForWebContents(content::WebContents* contents, PopupTracker* PopupTracker::CreateForWebContents(content::WebContents* contents,
content::WebContents* opener) { content::WebContents* opener) {
DCHECK(contents); DCHECK(contents);
DCHECK(opener); DCHECK(opener);
if (!FromWebContents(contents)) { auto* tracker = FromWebContents(contents);
contents->SetUserData(UserDataKey(), if (!tracker) {
base::WrapUnique(new PopupTracker(contents, opener))); tracker = new PopupTracker(contents, opener);
contents->SetUserData(UserDataKey(), base::WrapUnique(tracker));
} }
return tracker;
} }
PopupTracker::~PopupTracker() = default; PopupTracker::~PopupTracker() = default;
...@@ -61,10 +63,16 @@ void PopupTracker::WebContentsDestroyed() { ...@@ -61,10 +63,16 @@ void PopupTracker::WebContentsDestroyed() {
} }
if (opener_source_id_ != ukm::kInvalidSourceId) { if (opener_source_id_ != ukm::kInvalidSourceId) {
const int kMaxInteractions = 100;
int capped_interactions = num_interactions_ > kMaxInteractions
? kMaxInteractions
: num_interactions_;
ukm::builders::Popup_Closed(opener_source_id_) ukm::builders::Popup_Closed(opener_source_id_)
.SetEngagementTime(ukm::GetExponentialBucketMinForUserTiming( .SetEngagementTime(ukm::GetExponentialBucketMinForUserTiming(
total_foreground_duration.InMilliseconds())) total_foreground_duration.InMilliseconds()))
.SetUserInitiatedClose(web_contents()->GetClosedByUserGesture()) .SetUserInitiatedClose(web_contents()->GetClosedByUserGesture())
.SetTrusted(is_trusted_)
.SetNumInteractions(capped_interactions)
.Record(ukm::UkmRecorder::Get()); .Record(ukm::UkmRecorder::Get());
} }
} }
...@@ -93,3 +101,10 @@ void PopupTracker::OnVisibilityChanged(content::Visibility visibility) { ...@@ -93,3 +101,10 @@ void PopupTracker::OnVisibilityChanged(content::Visibility visibility) {
else else
visibility_tracker_.OnShown(); visibility_tracker_.OnShown();
} }
void PopupTracker::DidGetUserInteraction(
const blink::WebInputEvent::Type type) {
// TODO(csharrison): It would be nice if ctrl-W could be filtered out here,
// but the initial ctrl key press is registered as a kRawKeyDown.
num_interactions_++;
}
...@@ -26,10 +26,12 @@ class ScopedVisibilityTracker; ...@@ -26,10 +26,12 @@ class ScopedVisibilityTracker;
class PopupTracker : public content::WebContentsObserver, class PopupTracker : public content::WebContentsObserver,
public content::WebContentsUserData<PopupTracker> { public content::WebContentsUserData<PopupTracker> {
public: public:
static void CreateForWebContents(content::WebContents* contents, static PopupTracker* CreateForWebContents(content::WebContents* contents,
content::WebContents* opener); content::WebContents* opener);
~PopupTracker() override; ~PopupTracker() override;
void set_is_trusted(bool is_trusted) { is_trusted_ = is_trusted; }
private: private:
friend class content::WebContentsUserData<PopupTracker>; friend class content::WebContentsUserData<PopupTracker>;
...@@ -40,6 +42,7 @@ class PopupTracker : public content::WebContentsObserver, ...@@ -40,6 +42,7 @@ class PopupTracker : public content::WebContentsObserver,
void DidFinishNavigation( void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override; content::NavigationHandle* navigation_handle) override;
void OnVisibilityChanged(content::Visibility visibility) override; void OnVisibilityChanged(content::Visibility visibility) override;
void DidGetUserInteraction(const blink::WebInputEvent::Type type) override;
// Will be unset until the first navigation commits. Will be set to the total // Will be unset until the first navigation commits. Will be set to the total
// time the contents was visible at commit time. // time the contents was visible at commit time.
...@@ -50,10 +53,16 @@ class PopupTracker : public content::WebContentsObserver, ...@@ -50,10 +53,16 @@ class PopupTracker : public content::WebContentsObserver,
ScopedVisibilityTracker visibility_tracker_; ScopedVisibilityTracker visibility_tracker_;
// The number of user interactions occuring in this popup tab.
int num_interactions_ = 0;
// The id of the web contents that created the popup at the time of creation. // 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 // SourceIds are permanent so it's okay to use at any point so long as it's
// not invalid. // not invalid.
const ukm::SourceId opener_source_id_; const ukm::SourceId opener_source_id_;
bool is_trusted_ = false;
DISALLOW_COPY_AND_ASSIGN(PopupTracker); DISALLOW_COPY_AND_ASSIGN(PopupTracker);
}; };
......
...@@ -41,6 +41,8 @@ const char kPopupGestureClose[] = ...@@ -41,6 +41,8 @@ const char kPopupGestureClose[] =
const char kUkmEngagementTime[] = "EngagementTime"; const char kUkmEngagementTime[] = "EngagementTime";
const char kUkmUserInitiatedClose[] = "UserInitiatedClose"; const char kUkmUserInitiatedClose[] = "UserInitiatedClose";
const char kUkmTrusted[] = "Trusted";
const char kUkmNumInteractions[] = "NumInteractions";
} // namespace } // namespace
using UkmEntry = ukm::builders::Popup_Closed; using UkmEntry = ukm::builders::Popup_Closed;
...@@ -66,17 +68,14 @@ class PopupTrackerBrowserTest : public InProcessBrowserTest { ...@@ -66,17 +68,14 @@ class PopupTrackerBrowserTest : public InProcessBrowserTest {
return test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName).size(); return test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName).size();
} }
void VerifyUkm(const GURL& expected_url, const ukm::mojom::UkmEntry* ExpectAndGetEntry(const GURL& expected_url) {
UserClosedPopup expected_user_closed) {
const auto& entries = const auto& entries =
test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName); test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName);
ASSERT_EQ(1u, entries.size()); EXPECT_EQ(1u, entries.size());
const auto* entry = entries[0]; const auto* entry = entries[0];
test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, expected_url); test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, expected_url);
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(entry, kUkmEngagementTime)); EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(entry, kUkmEngagementTime));
test_ukm_recorder_->ExpectEntryMetric( return entry;
entry, kUkmUserInitiatedClose,
expected_user_closed == UserClosedPopup::kTrue ? 1u : 0u);
} }
std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_; std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
...@@ -126,7 +125,51 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ...@@ -126,7 +125,51 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest,
tester.ExpectTotalCount(kPopupEngagement, 1); tester.ExpectTotalCount(kPopupEngagement, 1);
tester.ExpectTotalCount(kPopupGestureClose, 1); tester.ExpectTotalCount(kPopupGestureClose, 1);
VerifyUkm(first_url, UserClosedPopup::kTrue); auto* entry = ExpectAndGetEntry(first_url);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmUserInitiatedClose, 1u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmTrusted, 0u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmNumInteractions, 0u);
}
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest,
WindowOpenPopup_WithInteraction) {
base::HistogramTester tester;
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();
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.open('/title1.html')"));
navigation_observer.Wait();
EXPECT_EQ(2, browser()->tab_strip_model()->count());
content::WebContents* popup =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(PopupTracker::FromWebContents(popup));
// Perform some user gestures on the page.
content::SimulateMouseClick(popup, 0, blink::WebMouseEvent::Button::kLeft);
content::SimulateMouseClick(popup, 0, blink::WebMouseEvent::Button::kLeft);
content::SimulateMouseClick(popup, 0, blink::WebMouseEvent::Button::kLeft);
// Close the popup and check metric.
int active_index = browser()->tab_strip_model()->active_index();
content::WebContentsDestroyedWatcher destroyed_watcher(popup);
browser()->tab_strip_model()->CloseWebContentsAt(
active_index, TabStripModel::CLOSE_USER_GESTURE);
destroyed_watcher.Wait();
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1);
tester.ExpectTotalCount(kPopupGestureClose, 1);
auto* entry = ExpectAndGetEntry(first_url);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmUserInitiatedClose, 1u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmTrusted, 0u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmNumInteractions, 3u);
} }
// OpenURLFromTab goes through a different code path than traditional popups // OpenURLFromTab goes through a different code path than traditional popups
...@@ -166,7 +209,10 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ControlClick_HasTracker) { ...@@ -166,7 +209,10 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ControlClick_HasTracker) {
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1); tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1); tester.ExpectTotalCount(kPopupEngagement, 1);
VerifyUkm(url, UserClosedPopup::kFalse); auto* entry = ExpectAndGetEntry(url);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmUserInitiatedClose, 0u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmTrusted, 1u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmNumInteractions, 0u);
} }
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ShiftClick_HasTracker) { IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ShiftClick_HasTracker) {
...@@ -200,7 +246,10 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ShiftClick_HasTracker) { ...@@ -200,7 +246,10 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, ShiftClick_HasTracker) {
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1); tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1); tester.ExpectTotalCount(kPopupEngagement, 1);
VerifyUkm(url, UserClosedPopup::kFalse); auto* entry = ExpectAndGetEntry(url);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmUserInitiatedClose, 0u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmTrusted, 1u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmNumInteractions, 0u);
} }
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, WhitelistedPopup_HasTracker) { IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, WhitelistedPopup_HasTracker) {
...@@ -233,7 +282,10 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, WhitelistedPopup_HasTracker) { ...@@ -233,7 +282,10 @@ IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, WhitelistedPopup_HasTracker) {
tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1); tester.ExpectTotalCount(kPopupFirstDocumentEngagement, 1);
tester.ExpectTotalCount(kPopupEngagement, 1); tester.ExpectTotalCount(kPopupEngagement, 1);
VerifyUkm(url, UserClosedPopup::kFalse); auto* entry = ExpectAndGetEntry(url);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmUserInitiatedClose, 0u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmTrusted, 1u);
test_ukm_recorder_->ExpectEntryMetric(entry, kUkmNumInteractions, 0u);
} }
IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, NoOpener_NoTracker) { IN_PROC_BROWSER_TEST_F(PopupTrackerBrowserTest, NoOpener_NoTracker) {
......
...@@ -1351,9 +1351,12 @@ WebContents* Browser::OpenURLFromTab(WebContents* source, ...@@ -1351,9 +1351,12 @@ WebContents* Browser::OpenURLFromTab(WebContents* source,
Navigate(&nav_params); Navigate(&nav_params);
if (is_popup && nav_params.navigated_or_inserted_contents) if (is_popup && nav_params.navigated_or_inserted_contents) {
PopupTracker::CreateForWebContents( auto* tracker = PopupTracker::CreateForWebContents(
nav_params.navigated_or_inserted_contents, source); nav_params.navigated_or_inserted_contents, source);
tracker->set_is_trusted(params.triggering_event_info !=
blink::WebTriggeringEventInfo::kFromUntrustedEvent);
}
return nav_params.navigated_or_inserted_contents; return nav_params.navigated_or_inserted_contents;
} }
......
...@@ -4061,6 +4061,20 @@ be describing additional metrics about the same event. ...@@ -4061,6 +4061,20 @@ be describing additional metrics about the same event.
popup opener's URL (not the URL of the popup). popup opener's URL (not the URL of the popup).
</summary> </summary>
</metric> </metric>
<metric name="NumInteractions">
<summary>
Integer value representing how many user interactions the popup had. This
is capped at 100. See IsUserInteractionInputType in web_contents_impl for
what an interaction is. Currently it is a mouse, scroll begin, touch
start, or raw key down event.
</summary>
</metric>
<metric name="Trusted">
<summary>
Boolean value representing whether the popup was opened via some trusted
event (e.g. context menu, ctrl-click, etc).
</summary>
</metric>
<metric name="UserInitiatedClose"> <metric name="UserInitiatedClose">
<summary> <summary>
Boolean value to represent whether the popup was closed by user gesture. Boolean value to represent whether the popup was closed by user gesture.
......
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