Commit e730ea4c authored by Yao Xiao's avatar Yao Xiao Committed by Commit Bot

Add UseCounter for ad clicks on page

Besides, as a result of exploring different approaches, this CL additionally
adds FromAdState enum that can be potentially shared between WindowOpen and
other event. Made the testing logic easier to follow by switching
to parameter test and expecting single UMA/UKM entry per test.

Bug: 897256
Change-Id: Ic3f0e3637de77968f15e17f25d340d2456f988ef
Reviewed-on: https://chromium-review.googlesource.com/c/1292369
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Reviewed-by: default avatarBryan McQuade <bmcquade@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606226}
parent 5b2ec23b
......@@ -61,7 +61,7 @@ bool IsAllowedUkmFeature(blink::mojom::WebFeature feature) {
WebFeature::kSuppressHistoryEntryWithoutUserGesture,
WebFeature::kCursorImageGT32x32, WebFeature::kCursorImageLE32x32,
WebFeature::kHistoryPushState, WebFeature::kHistoryReplaceState,
WebFeature::kCursorImageGT64x64,
WebFeature::kCursorImageGT64x64, WebFeature::kAdClick,
}));
return opt_in_features->count(feature);
}
......@@ -22,6 +22,7 @@
#include "content/public/test/test_navigation_observer.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/frame/from_ad_state.h"
#include "url/gurl.h"
namespace subresource_filter {
......@@ -343,15 +344,14 @@ const ukm::mojom::UkmEntry* FindDocumentCreatedEntry(
return nullptr;
}
void ExpectLatestWindowOpenUkmEntry(const ukm::TestUkmRecorder& ukm_recorder,
size_t expected_num_entries,
bool from_main_frame,
const GURL& main_frame_url,
bool from_ad_subframe,
bool from_ad_script) {
void ExpectWindowOpenUkmEntry(const ukm::TestUkmRecorder& ukm_recorder,
bool from_main_frame,
const GURL& main_frame_url,
bool from_ad_subframe,
bool from_ad_script) {
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::AbusiveExperienceHeuristic_WindowOpen::kEntryName);
EXPECT_EQ(expected_num_entries, entries.size());
EXPECT_EQ(1u, entries.size());
// Check that the event is keyed to |main_frame_url| only if it was from the
// top frame.
......@@ -392,32 +392,30 @@ void ExpectLatestWindowOpenUkmEntry(const ukm::TestUkmRecorder& ukm_recorder,
from_ad_script);
}
void ExpectWindowOpenUmaStatus(
const base::HistogramTester& histogram_tester,
base::HistogramBase::Count expected_num_from_adscript_adframe,
base::HistogramBase::Count expected_num_from_nonadscript_adframe,
base::HistogramBase::Count expected_num_from_adscript_nonadframe,
base::HistogramBase::Count expected_num_from_nonadscript_nonadframe) {
void ExpectWindowOpenUmaEntry(const base::HistogramTester& histogram_tester,
bool from_ad_subframe,
bool from_ad_script) {
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester.ExpectBucketCount(
kWindowOpenFromAdStateHistogram,
0 /* blink::WindowOpenFromAdState::kAdScriptAndAdFrame */,
expected_num_from_adscript_adframe);
histogram_tester.ExpectBucketCount(
kWindowOpenFromAdStateHistogram,
1 /* blink::WindowOpenFromAdState::kNonAdScriptAndAdFrame */,
expected_num_from_nonadscript_adframe);
histogram_tester.ExpectBucketCount(
kWindowOpenFromAdStateHistogram,
2 /* blink::WindowOpenFromAdState::kAdScriptAndNonAdFrame */,
expected_num_from_adscript_nonadframe);
histogram_tester.ExpectBucketCount(
kWindowOpenFromAdStateHistogram,
3 /* blink::WindowOpenFromAdState::kNonAdScriptAndNonAdFrame */,
expected_num_from_nonadscript_nonadframe);
blink::FromAdState state =
blink::GetFromAdState(from_ad_subframe, from_ad_script);
histogram_tester.ExpectBucketCount(kWindowOpenFromAdStateHistogram, state,
1 /* expected_count */);
}
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, WindowOpenFromSubframe) {
class AdTaggingEventFromSubframeBrowserTest
: public AdTaggingBrowserTest,
public ::testing::WithParamInterface<
std::tuple<bool /* cross_origin */, bool /* from_ad_subframe */>> {};
IN_PROC_BROWSER_TEST_P(AdTaggingEventFromSubframeBrowserTest,
WindowOpenFromSubframe) {
bool cross_origin;
bool from_ad_subframe;
std::tie(cross_origin, from_ad_subframe) = GetParam();
SCOPED_TRACE(::testing::Message()
<< "cross_origin = " << cross_origin << ", "
<< "from_ad_subframe = " << from_ad_subframe);
ukm::TestAutoSetUkmRecorder ukm_recorder;
base::HistogramTester histogram_tester;
GURL main_frame_url =
......@@ -425,60 +423,56 @@ IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, WindowOpenFromSubframe) {
ui_test_utils::NavigateToURL(browser(), main_frame_url);
content::WebContents* main_tab = GetWebContents();
size_t expected_num_entries = 0;
size_t expected_num_from_ad_subframe = 0;
for (bool cross_origin : {false, true}) {
for (bool ad_frame : {false, true}) {
std::string hostname = cross_origin ? "b.com" : "a.com";
std::string suffix = ad_frame ? "&ad=true" : "";
SCOPED_TRACE(::testing::Message()
<< "cross_origin = " << cross_origin << ", "
<< "ad_frame = " << ad_frame);
RenderFrameHost* child = CreateSrcFrame(
main_tab, embedded_test_server()->GetURL(
hostname, "/ad_tagging/frame_factory.html?1" + suffix));
EXPECT_TRUE(content::ExecuteScript(child, "window.open();"));
++expected_num_entries;
if (ad_frame)
++expected_num_from_ad_subframe;
ExpectLatestWindowOpenUkmEntry(
ukm_recorder, expected_num_entries, false /* from_main_frame */,
main_frame_url, ad_frame /* from_ad_subframe */,
ad_frame /* from_ad_script */);
ExpectWindowOpenUmaStatus(
histogram_tester,
expected_num_from_ad_subframe /* adscript_adframe */,
0 /* nonadscript_adframe */, 0 /* adscript_nonadframe */,
expected_num_entries -
expected_num_from_ad_subframe /* nonadscript_nonadframe */);
}
}
std::string hostname = cross_origin ? "b.com" : "a.com";
std::string suffix = from_ad_subframe ? "&ad=true" : "";
RenderFrameHost* child = CreateSrcFrame(
main_tab, embedded_test_server()->GetURL(
hostname, "/ad_tagging/frame_factory.html?1" + suffix));
EXPECT_TRUE(content::ExecuteScript(child, "window.open();"));
bool from_ad_script = from_ad_subframe;
ExpectWindowOpenUkmEntry(ukm_recorder, false /* from_main_frame */,
main_frame_url, from_ad_subframe, from_ad_script);
ExpectWindowOpenUmaEntry(histogram_tester, from_ad_subframe, from_ad_script);
}
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, WindowOpenWithScriptInStack) {
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
AdTaggingEventFromSubframeBrowserTest,
::testing::Combine(::testing::Bool(), ::testing::Bool()));
class AdTaggingEventWithScriptInStackBrowserTest
: public AdTaggingBrowserTest,
public ::testing::WithParamInterface<bool /* from_ad_script */> {};
IN_PROC_BROWSER_TEST_P(AdTaggingEventWithScriptInStackBrowserTest,
WindowOpenWithScriptInStack) {
bool from_ad_script = GetParam();
SCOPED_TRACE(::testing::Message() << "from_ad_script = " << from_ad_script);
ukm::TestAutoSetUkmRecorder ukm_recorder;
base::HistogramTester histogram_tester;
GURL main_frame_url = GetURL("frame_factory.html");
ui_test_utils::NavigateToURL(browser(), main_frame_url);
content::WebContents* main_tab = GetWebContents();
EXPECT_TRUE(content::ExecuteScript(main_tab, "windowOpenFromNonAdScript();"));
ExpectLatestWindowOpenUkmEntry(
ukm_recorder, 1 /* expected_num_entries */, true /* from_main_frame */,
main_frame_url, false /* from_ad_subframe */, false /* from_ad_script */);
ExpectWindowOpenUmaStatus(
histogram_tester, 0 /* adscript_adframe */, 0 /* nonadscript_adframe */,
0 /* adscript_nonadframe */, 1 /* nonadscript_nonadframe */);
EXPECT_TRUE(content::ExecuteScript(main_tab, "windowOpenFromAdScript();"));
ExpectLatestWindowOpenUkmEntry(
ukm_recorder, 2 /* expected_num_entries */, true /* from_main_frame */,
main_frame_url, false /* from_ad_subframe */, true /* from_ad_script */);
ExpectWindowOpenUmaStatus(
histogram_tester, 0 /* adscript_adframe */, 0 /* nonadscript_adframe */,
1 /* adscript_nonadframe */, 1 /* nonadscript_nonadframe */);
std::string script = from_ad_script ? "windowOpenFromAdScript();"
: "windowOpenFromNonAdScript();";
EXPECT_TRUE(content::ExecuteScript(main_tab, script));
bool from_ad_subframe = false;
ExpectWindowOpenUkmEntry(ukm_recorder, true /* from_main_frame */,
main_frame_url, from_ad_subframe, from_ad_script);
ExpectWindowOpenUmaEntry(histogram_tester, from_ad_subframe, from_ad_script);
}
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
AdTaggingEventWithScriptInStackBrowserTest,
::testing::Bool());
} // namespace
} // namespace subresource_filter
......@@ -31,6 +31,7 @@ jumbo_source_set("common") {
"feature_policy/feature_policy.cc",
"features.cc",
"frame/frame_policy.cc",
"frame/from_ad_state.cc",
"frame/user_activation_state.cc",
"indexeddb/indexeddb_key.cc",
"indexeddb/indexeddb_key_path.cc",
......
// Copyright 2018 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 "third_party/blink/public/common/frame/from_ad_state.h"
namespace blink {
FromAdState GetFromAdState(bool is_ad_subframe, bool is_ad_script_in_stack) {
return is_ad_subframe
? is_ad_script_in_stack ? FromAdState::kAdScriptAndAdFrame
: FromAdState::kNonAdScriptAndAdFrame
: is_ad_script_in_stack ? FromAdState::kAdScriptAndNonAdFrame
: FromAdState::kNonAdScriptAndNonAdFrame;
}
} // namespace blink
......@@ -47,6 +47,7 @@ source_set("headers") {
"features.h",
"frame/frame_owner_element_type.h",
"frame/frame_policy.h",
"frame/from_ad_state.h",
"frame/sandbox_flags.h",
"frame/user_activation_state.h",
"frame/user_activation_update_source.h",
......
// Copyright 2018 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 THIRD_PARTY_BLINK_PUBLIC_COMMON_FRAME_FROM_AD_STATE_H_
#define THIRD_PARTY_BLINK_PUBLIC_COMMON_FRAME_FROM_AD_STATE_H_
#include "third_party/blink/public/common/common_export.h"
namespace blink {
// This enum is the cross product of two ad related status of an event: whether
// the event occurs on an ad frame, and whether it occurs with an ad script in
// the stack.
enum class FromAdState {
// This is used for a UMA histogram. Please never alter existing values, only
// append new ones and make sure to update enums.xml.
kAdScriptAndAdFrame = 0,
kNonAdScriptAndAdFrame = 1,
kAdScriptAndNonAdFrame = 2,
kNonAdScriptAndNonAdFrame = 3,
kMaxValue = kNonAdScriptAndNonAdFrame,
};
// Returns the FromAdState corresponded to the cross product of |is_ad_subframe|
// and |is_ad_script_in_stack|.
FromAdState BLINK_COMMON_EXPORT GetFromAdState(bool is_ad_subframe,
bool is_ad_script_in_stack);
} // namespace blink
#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_FRAME_FROM_AD_STATE_H_
......@@ -2070,6 +2070,7 @@ enum WebFeature {
kHistoryReplaceState = 2618,
kGetDisplayMedia = 2619,
kCursorImageGT64x64 = 2620,
kAdClick = 2621,
// Add new features immediately above this line. Don't change assigned
// numbers of any item, and don't reuse removed slots.
// Also, run update_use_counter_feature_enum.py in
......
......@@ -37,6 +37,7 @@
#include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
#include "third_party/blink/renderer/core/dom/events/window_event_context.h"
#include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/frame/ad_tracker.h"
#include "third_party/blink/renderer/core/frame/deprecation.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
......@@ -150,13 +151,29 @@ DispatchEventResult EventDispatcher::Dispatch() {
}
event_->GetEventPath().EnsureWindowEventContext();
const bool is_click =
event_->IsMouseEvent() && event_->type() == event_type_names::kClick;
if (is_click && event_->isTrusted()) {
Document& document = node_->GetDocument();
LocalFrame* frame = document.GetFrame();
if (frame) {
// A genuine mouse click cannot be triggered by script so we don't expect
// there are any script in the stack.
DCHECK(!frame->GetAdTracker() ||
!frame->GetAdTracker()->IsAdScriptInStack());
if (frame->IsAdSubframe()) {
UseCounter::Count(document, WebFeature::kAdClick);
}
}
}
// 6. Let isActivationEvent be true, if event is a MouseEvent object and
// event's type attribute is "click", and false otherwise.
//
// We need to include non-standard textInput event for HTMLInputElement.
const bool is_activation_event =
(event_->IsMouseEvent() && event_->type() == event_type_names::kClick) ||
event_->type() == event_type_names::kTextInput;
is_click || event_->type() == event_type_names::kTextInput;
// 7. Let activationTarget be target, if isActivationEvent is true and target
// has activation behavior, and null otherwise.
......
......@@ -32,6 +32,7 @@
#include "services/network/public/mojom/request_context_frame_type.mojom-blink.h"
#include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/frame/from_ad_state.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/web/web_view_client.h"
......@@ -205,33 +206,17 @@ static Frame* ReuseExistingWindow(LocalFrame& active_frame,
return nullptr;
}
enum class WindowOpenFromAdState {
// This is used for a UMA histogram. Please never alter existing values, only
// append new ones and make sure to update enums.xml.
kAdScriptAndAdFrame = 0,
kNonAdScriptAndAdFrame = 1,
kAdScriptAndNonAdFrame = 2,
kNonAdScriptAndNonAdFrame = 3,
kMaxValue = kNonAdScriptAndNonAdFrame,
};
static void MaybeLogWindowOpen(LocalFrame& opener_frame) {
AdTracker* ad_tracker = opener_frame.GetAdTracker();
if (!ad_tracker) {
if (!ad_tracker)
return;
}
bool is_ad_subframe = opener_frame.IsAdSubframe();
bool is_ad_script_in_stack = ad_tracker->IsAdScriptInStack();
FromAdState state =
blink::GetFromAdState(is_ad_subframe, is_ad_script_in_stack);
// Log to UMA.
WindowOpenFromAdState state =
is_ad_subframe ? (is_ad_script_in_stack
? WindowOpenFromAdState::kAdScriptAndAdFrame
: WindowOpenFromAdState::kNonAdScriptAndAdFrame)
: (is_ad_script_in_stack
? WindowOpenFromAdState::kAdScriptAndNonAdFrame
: WindowOpenFromAdState::kNonAdScriptAndNonAdFrame);
UMA_HISTOGRAM_ENUMERATION("Blink.WindowOpen.FromAdState", state);
// Log to UKM.
......
......@@ -20661,6 +20661,7 @@ Called by update_net_error_codes.py.-->
<int value="2618" label="HistoryReplaceState"/>
<int value="2619" label="GetDisplayMedia"/>
<int value="2620" label="CursorImageGT64x64"/>
<int value="2621" label="AdClick"/>
</enum>
<enum name="FeaturePolicyFeature">
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