Commit a8360926 authored by Dominic Battre's avatar Dominic Battre Committed by Commit Bot

Delay binding of SourceId to URL in PasswordFormMetricsRecorder

This CL delays the binding of a ukm::SourceID to a URL in the
PasswordFormMetricsRecorder until the recorder is destroyed. The reason for
this is that long-lived tabs/WebContents would bind the URL at creation. But by
destruction (i.e. reporting time) the binding is purged and the data is
discarded.

Bug: 732846
Change-Id: I0a7c654ddc1a866e4abbe51206f62949500b5ba9
Reviewed-on: https://chromium-review.googlesource.com/577887
Commit-Queue: Dominic Battré <battre@chromium.org>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#488214}
parent 28dfaa0d
...@@ -185,13 +185,9 @@ TEST_P(SavePasswordInfoBarDelegateTestForUKMs, VerifyUKMRecording) { ...@@ -185,13 +185,9 @@ TEST_P(SavePasswordInfoBarDelegateTestForUKMs, VerifyUKMRecording) {
ukm::TestUkmRecorder test_ukm_recorder; ukm::TestUkmRecorder test_ukm_recorder;
{ {
// Setup metrics recorder // Setup metrics recorder
ukm::SourceId source_id = test_ukm_recorder.GetNewSourceID();
static_cast<ukm::UkmRecorder*>(&test_ukm_recorder)
->UpdateSourceURL(source_id, GURL("https://www.example.com/"));
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>(
true /*is_main_frame_secure*/, true /*is_main_frame_secure*/, &test_ukm_recorder,
PasswordFormMetricsRecorder::CreateUkmEntryBuilder(&test_ukm_recorder, test_ukm_recorder.GetNewSourceID(), GURL("https://www.example.com/"));
source_id));
// Exercise delegate. // Exercise delegate.
std::unique_ptr<MockPasswordFormManager> password_form_manager( std::unique_ptr<MockPasswordFormManager> password_form_manager(
......
...@@ -334,13 +334,10 @@ TEST_F(ManagePasswordsBubbleModelTest, Edit) { ...@@ -334,13 +334,10 @@ TEST_F(ManagePasswordsBubbleModelTest, Edit) {
{ {
// Setup metrics recorder // Setup metrics recorder
ukm::SourceId source_id = test_ukm_recorder.GetNewSourceID(); ukm::SourceId source_id = test_ukm_recorder.GetNewSourceID();
static_cast<ukm::UkmRecorder*>(&test_ukm_recorder) auto recorder =
->UpdateSourceURL(source_id, GURL("https://www.example.com/")); base::MakeRefCounted<password_manager::PasswordFormMetricsRecorder>(
auto recorder = base::MakeRefCounted< true /*is_main_frame_secure*/, &test_ukm_recorder, source_id,
password_manager::PasswordFormMetricsRecorder>( GURL("https://www.example.com/"));
true /*is_main_frame_secure*/,
password_manager::PasswordFormMetricsRecorder::CreateUkmEntryBuilder(
&test_ukm_recorder, source_id));
// Exercise bubble. // Exercise bubble.
ON_CALL(*controller(), GetPasswordFormMetricsRecorder()) ON_CALL(*controller(), GetPasswordFormMetricsRecorder())
...@@ -571,14 +568,11 @@ TEST_F(ManagePasswordsBubbleModelTest, RecordUKMs) { ...@@ -571,14 +568,11 @@ TEST_F(ManagePasswordsBubbleModelTest, RecordUKMs) {
ukm::TestUkmRecorder test_ukm_recorder; ukm::TestUkmRecorder test_ukm_recorder;
{ {
// Setup metrics recorder // Setup metrics recorder
ukm::SourceId source_id = test_ukm_recorder.GetNewSourceID();
static_cast<ukm::UkmRecorder*>(&test_ukm_recorder)
->UpdateSourceURL(source_id, GURL("https://www.example.com/"));
auto recorder = base::MakeRefCounted< auto recorder = base::MakeRefCounted<
password_manager::PasswordFormMetricsRecorder>( password_manager::PasswordFormMetricsRecorder>(
true /*is_main_frame_secure*/, true /*is_main_frame_secure*/, &test_ukm_recorder,
password_manager::PasswordFormMetricsRecorder:: test_ukm_recorder.GetNewSourceID(),
CreateUkmEntryBuilder(&test_ukm_recorder, source_id)); GURL("https://www.example.com/"));
// Exercise bubble. // Exercise bubble.
ON_CALL(*controller(), GetPasswordFormMetricsRecorder()) ON_CALL(*controller(), GetPasswordFormMetricsRecorder())
......
...@@ -255,9 +255,8 @@ void PasswordFormManager::Init( ...@@ -255,9 +255,8 @@ void PasswordFormManager::Init(
metrics_recorder_ = std::move(metrics_recorder); metrics_recorder_ = std::move(metrics_recorder);
if (!metrics_recorder_) { if (!metrics_recorder_) {
metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>( metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
client_->IsMainFrameSecure(), client_->IsMainFrameSecure(), client_->GetUkmRecorder(),
PasswordFormMetricsRecorder::CreateUkmEntryBuilder( client_->GetUkmSourceId(), client_->GetMainFrameURL());
client_->GetUkmRecorder(), client_->GetUkmSourceId()));
} }
if (owned_form_fetcher_) if (owned_form_fetcher_)
......
...@@ -3843,21 +3843,22 @@ TEST_F(PasswordFormManagerTest, TestUkmForFilling) { ...@@ -3843,21 +3843,22 @@ TEST_F(PasswordFormManagerTest, TestUkmForFilling) {
fetched_forms.push_back(test.fetched_form); fetched_forms.push_back(test.fetched_form);
ukm::TestUkmRecorder test_ukm_recorder; ukm::TestUkmRecorder test_ukm_recorder;
client()->GetUkmRecorder()->UpdateSourceURL(client()->GetUkmSourceId(),
form_to_fill.origin);
{ {
auto metrics_recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>(
form_to_fill.origin.SchemeIsCryptographic(), &test_ukm_recorder,
test_ukm_recorder.GetNewSourceID(), form_to_fill.origin);
FakeFormFetcher fetcher; FakeFormFetcher fetcher;
PasswordFormManager form_manager( PasswordFormManager form_manager(
password_manager(), client(), password_manager(), client(),
test.is_http_basic_auth ? nullptr : client()->driver(), form_to_fill, test.is_http_basic_auth ? nullptr : client()->driver(), form_to_fill,
base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher); base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
form_manager.Init(nullptr); form_manager.Init(metrics_recorder);
fetcher.SetNonFederated(fetched_forms, 0u); fetcher.SetNonFederated(fetched_forms, 0u);
} }
const auto* source = const auto* source =
test_ukm_recorder.GetSourceForUrl(form_to_fill.origin.spec().c_str()); test_ukm_recorder.GetSourceForUrl(form_to_fill.origin.spec().c_str());
ASSERT_TRUE(source);
test_ukm_recorder.ExpectMetric(*source, "PasswordForm", test_ukm_recorder.ExpectMetric(*source, "PasswordForm",
kUkmManagerFillEvent, test.expected_event); kUkmManagerFillEvent, test.expected_event);
} }
......
...@@ -32,9 +32,17 @@ const char kUkmUserAction[] = "User.Action"; ...@@ -32,9 +32,17 @@ const char kUkmUserAction[] = "User.Action";
PasswordFormMetricsRecorder::PasswordFormMetricsRecorder( PasswordFormMetricsRecorder::PasswordFormMetricsRecorder(
bool is_main_frame_secure, bool is_main_frame_secure,
std::unique_ptr<ukm::UkmEntryBuilder> ukm_entry_builder) ukm::UkmRecorder* ukm_recorder,
ukm::SourceId source_id,
const GURL& main_frame_url)
: is_main_frame_secure_(is_main_frame_secure), : is_main_frame_secure_(is_main_frame_secure),
ukm_entry_builder_(std::move(ukm_entry_builder)) {} ukm_recorder_(ukm_recorder),
source_id_(source_id),
main_frame_url_(main_frame_url),
ukm_entry_builder_(
ukm_recorder
? ukm_recorder->GetEntryBuilder(source_id, "PasswordForm")
: nullptr) {}
PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() { PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() {
UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenV3", GetActionsTaken(), UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenV3", GetActionsTaken(),
...@@ -76,16 +84,11 @@ PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() { ...@@ -76,16 +84,11 @@ PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() {
for (const DetailedUserAction& action : one_time_report_user_actions_) for (const DetailedUserAction& action : one_time_report_user_actions_)
RecordUkmMetric(kUkmUserAction, static_cast<int64_t>(action)); RecordUkmMetric(kUkmUserAction, static_cast<int64_t>(action));
}
// static // Bind |main_frame_url_| to |source_id_| directly before sending the content
std::unique_ptr<ukm::UkmEntryBuilder> // of |ukm_recorder_| to ensure that the binding has not been purged already.
PasswordFormMetricsRecorder::CreateUkmEntryBuilder( if (ukm_recorder_)
ukm::UkmRecorder* ukm_recorder, ukm_recorder_->UpdateSourceURL(source_id_, main_frame_url_);
ukm::SourceId source_id) {
if (!ukm_recorder)
return nullptr;
return ukm_recorder->GetEntryBuilder(source_id, "PasswordForm");
} }
void PasswordFormMetricsRecorder::MarkGenerationAvailable() { void PasswordFormMetricsRecorder::MarkGenerationAvailable() {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "components/password_manager/core/browser/password_form_user_action.h" #include "components/password_manager/core/browser/password_form_user_action.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "services/metrics/public/cpp/ukm_recorder.h" #include "services/metrics/public/cpp/ukm_recorder.h"
#include "url/gurl.h"
namespace password_manager { namespace password_manager {
...@@ -96,19 +97,18 @@ class FormFetcher; ...@@ -96,19 +97,18 @@ class FormFetcher;
class PasswordFormMetricsRecorder class PasswordFormMetricsRecorder
: public base::RefCounted<PasswordFormMetricsRecorder> { : public base::RefCounted<PasswordFormMetricsRecorder> {
public: public:
// |ukm_entry_builder| is the destination into which UKM metrics are recorded. // Records UKM metrics and reports them on destruction. The |source_id| is
// It may be nullptr, in which case no UKM metrics are recorded. This should // (re-)bound to |main_frame_url| shortly before reporting. As such it is
// be created via the static CreateUkmEntryBuilder() method of this class. // crucial that the |source_id| is never bound to a different URL by another
PasswordFormMetricsRecorder( // consumer. The reason for this late binding is that metrics can be
bool is_main_frame_secure, // collected for a WebContents for a long period of time and by the time the
std::unique_ptr<ukm::UkmEntryBuilder> ukm_entry_builder); // reporting happens, the binding of |source_id| to |main_frame_url| is
// already purged. |ukm_recorder| may be a nullptr, in which case no UKM
// Creates a UkmEntryBuilder that can be used to record metrics into the event // metrics are recorded.
// "PasswordForm". |source_id| should be bound the the correct URL in the PasswordFormMetricsRecorder(bool is_main_frame_secure,
// |ukm_recorder| when this function is called. ukm::UkmRecorder* ukm_recorder,
static std::unique_ptr<ukm::UkmEntryBuilder> CreateUkmEntryBuilder( ukm::SourceId source_id,
ukm::UkmRecorder* ukm_recorder, const GURL& main_frame_url);
ukm::SourceId source_id);
// ManagerAction - What does the PasswordFormManager do with this form? Either // ManagerAction - What does the PasswordFormManager do with this form? Either
// it fills it, or it doesn't. If it doesn't fill it, that's either // it fills it, or it doesn't. If it doesn't fill it, that's either
...@@ -367,6 +367,18 @@ class PasswordFormMetricsRecorder ...@@ -367,6 +367,18 @@ class PasswordFormMetricsRecorder
// data the user has entered. // data the user has entered.
SubmittedFormType submitted_form_type_ = kSubmittedFormTypeUnspecified; SubmittedFormType submitted_form_type_ = kSubmittedFormTypeUnspecified;
// Recorder to which metrics are sent. Has to outlive this
// PasswordFormMetricsRecorder.
ukm::UkmRecorder* ukm_recorder_;
// A SourceId of |ukm_recorder_|. This id gets bound to |main_frame_url_| on
// destruction. It can be shared across multiple metrics recorders as long as
// they all bind it to the same URL.
ukm::SourceId source_id_;
// URL for which UKMs are reported.
GURL main_frame_url_;
// Records URL keyed metrics (UKMs) and submits them on its destruction. May // Records URL keyed metrics (UKMs) and submits them on its destruction. May
// be a nullptr in which case no recording is expected. // be a nullptr in which case no recording is expected.
std::unique_ptr<ukm::UkmEntryBuilder> ukm_entry_builder_; std::unique_ptr<ukm::UkmEntryBuilder> ukm_entry_builder_;
......
...@@ -22,13 +22,12 @@ namespace { ...@@ -22,13 +22,12 @@ namespace {
constexpr char kTestUrl[] = "https://www.example.com/"; constexpr char kTestUrl[] = "https://www.example.com/";
// Create a UkmEntryBuilder with a SourceId that is initialized for kTestUrl. // Create a UkmEntryBuilder with a SourceId that is initialized for kTestUrl.
std::unique_ptr<ukm::UkmEntryBuilder> CreateUkmEntryBuilder( scoped_refptr<PasswordFormMetricsRecorder> CreatePasswordFormMetricsRecorder(
bool is_main_frame_secure,
ukm::TestUkmRecorder* test_ukm_recorder) { ukm::TestUkmRecorder* test_ukm_recorder) {
ukm::SourceId source_id = test_ukm_recorder->GetNewSourceID(); return base::MakeRefCounted<PasswordFormMetricsRecorder>(
static_cast<ukm::UkmRecorder*>(test_ukm_recorder) is_main_frame_secure, test_ukm_recorder,
->UpdateSourceURL(source_id, GURL(kTestUrl)); test_ukm_recorder->GetNewSourceID(), GURL(kTestUrl));
return PasswordFormMetricsRecorder::CreateUkmEntryBuilder(test_ukm_recorder,
source_id);
} }
// TODO(crbug.com/738921) Replace this with generalized infrastructure. // TODO(crbug.com/738921) Replace this with generalized infrastructure.
...@@ -87,9 +86,8 @@ TEST(PasswordFormMetricsRecorder, Generation) { ...@@ -87,9 +86,8 @@ TEST(PasswordFormMetricsRecorder, Generation) {
// Use a scoped PasswordFromMetricsRecorder because some metrics are recored // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
// on destruction. // on destruction.
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
/*is_main_frame_secure*/ true, /*is_main_frame_secure*/ true, &test_ukm_recorder);
CreateUkmEntryBuilder(&test_ukm_recorder));
if (test.generation_available) if (test.generation_available)
recorder->MarkGenerationAvailable(); recorder->MarkGenerationAvailable();
recorder->SetHasGeneratedPassword(test.has_generated_password); recorder->SetHasGeneratedPassword(test.has_generated_password);
...@@ -247,8 +245,8 @@ TEST(PasswordFormMetricsRecorder, Actions) { ...@@ -247,8 +245,8 @@ TEST(PasswordFormMetricsRecorder, Actions) {
// Use a scoped PasswordFromMetricsRecorder because some metrics are recored // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
// on destruction. // on destruction.
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
test.is_main_frame_secure, CreateUkmEntryBuilder(&test_ukm_recorder)); test.is_main_frame_secure, &test_ukm_recorder);
recorder->SetManagerAction(test.manager_action); recorder->SetManagerAction(test.manager_action);
if (test.user_action != UserAction::kNone) if (test.user_action != UserAction::kNone)
...@@ -314,9 +312,8 @@ TEST(PasswordFormMetricsRecorder, ActionSequence) { ...@@ -314,9 +312,8 @@ TEST(PasswordFormMetricsRecorder, ActionSequence) {
// Use a scoped PasswordFromMetricsRecorder because some metrics are recored // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
// on destruction. // on destruction.
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
/*is_main_frame_secure*/ true, true /*is_main_frame_secure*/, &test_ukm_recorder);
CreateUkmEntryBuilder(&test_ukm_recorder));
recorder->SetManagerAction( recorder->SetManagerAction(
PasswordFormMetricsRecorder::kManagerActionAutofilled); PasswordFormMetricsRecorder::kManagerActionAutofilled);
recorder->SetUserAction(UserAction::kChoosePslMatch); recorder->SetUserAction(UserAction::kChoosePslMatch);
...@@ -360,8 +357,8 @@ TEST(PasswordFormMetricsRecorder, SubmittedFormType) { ...@@ -360,8 +357,8 @@ TEST(PasswordFormMetricsRecorder, SubmittedFormType) {
// Use a scoped PasswordFromMetricsRecorder because some metrics are recored // Use a scoped PasswordFromMetricsRecorder because some metrics are recored
// on destruction. // on destruction.
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
test.is_main_frame_secure, CreateUkmEntryBuilder(&test_ukm_recorder)); test.is_main_frame_secure, &test_ukm_recorder);
recorder->SetSubmittedFormType(test.form_type); recorder->SetSubmittedFormType(test.form_type);
} }
...@@ -447,9 +444,8 @@ TEST(PasswordFormMetricsRecorder, RecordPasswordBubbleShown) { ...@@ -447,9 +444,8 @@ TEST(PasswordFormMetricsRecorder, RecordPasswordBubbleShown) {
<< ", display_disposition = " << test.display_disposition); << ", display_disposition = " << test.display_disposition);
ukm::TestUkmRecorder test_ukm_recorder; ukm::TestUkmRecorder test_ukm_recorder;
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
true /*is_main_frame_secure*/, true /*is_main_frame_secure*/, &test_ukm_recorder);
CreateUkmEntryBuilder(&test_ukm_recorder));
recorder->RecordPasswordBubbleShown(test.credential_source_type, recorder->RecordPasswordBubbleShown(test.credential_source_type,
test.display_disposition); test.display_disposition);
} }
...@@ -505,9 +501,8 @@ TEST(PasswordFormMetricsRecorder, RecordUIDismissalReason) { ...@@ -505,9 +501,8 @@ TEST(PasswordFormMetricsRecorder, RecordUIDismissalReason) {
<< ", dismissal_reason = " << test.dismissal_reason); << ", dismissal_reason = " << test.dismissal_reason);
ukm::TestUkmRecorder test_ukm_recorder; ukm::TestUkmRecorder test_ukm_recorder;
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
true /*is_main_frame_secure*/, true /*is_main_frame_secure*/, &test_ukm_recorder);
CreateUkmEntryBuilder(&test_ukm_recorder));
recorder->RecordPasswordBubbleShown( recorder->RecordPasswordBubbleShown(
metrics_util::CredentialSourceType::kPasswordManager, metrics_util::CredentialSourceType::kPasswordManager,
test.display_disposition); test.display_disposition);
...@@ -530,9 +525,8 @@ TEST(PasswordFormMetricsRecorder, SequencesOfBubbles) { ...@@ -530,9 +525,8 @@ TEST(PasswordFormMetricsRecorder, SequencesOfBubbles) {
using BubbleTrigger = PasswordFormMetricsRecorder::BubbleTrigger; using BubbleTrigger = PasswordFormMetricsRecorder::BubbleTrigger;
ukm::TestUkmRecorder test_ukm_recorder; ukm::TestUkmRecorder test_ukm_recorder;
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
true /*is_main_frame_secure*/, true /*is_main_frame_secure*/, &test_ukm_recorder);
CreateUkmEntryBuilder(&test_ukm_recorder));
// Open and confirm an automatically triggered saving prompt. // Open and confirm an automatically triggered saving prompt.
recorder->RecordPasswordBubbleShown( recorder->RecordPasswordBubbleShown(
metrics_util::CredentialSourceType::kPasswordManager, metrics_util::CredentialSourceType::kPasswordManager,
...@@ -574,9 +568,8 @@ TEST(PasswordFormMetricsRecorder, RecordDetailedUserAction) { ...@@ -574,9 +568,8 @@ TEST(PasswordFormMetricsRecorder, RecordDetailedUserAction) {
const DetailedUserAction kRepeatedAction = DetailedUserAction::kUnknown; const DetailedUserAction kRepeatedAction = DetailedUserAction::kUnknown;
ukm::TestUkmRecorder test_ukm_recorder; ukm::TestUkmRecorder test_ukm_recorder;
{ {
auto recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>( auto recorder = CreatePasswordFormMetricsRecorder(
true /*is_main_frame_secure*/, true /*is_main_frame_secure*/, &test_ukm_recorder);
CreateUkmEntryBuilder(&test_ukm_recorder));
recorder->RecordDetailedUserAction(kOneTimeAction); recorder->RecordDetailedUserAction(kOneTimeAction);
recorder->RecordDetailedUserAction(kOneTimeAction); recorder->RecordDetailedUserAction(kOneTimeAction);
recorder->RecordDetailedUserAction(kRepeatedAction); recorder->RecordDetailedUserAction(kRepeatedAction);
......
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