Add Email Aliases metrics (#36469)

* Add metrics for Email Aliases

* Add notes and copy metrics for email aliases

* Presubmit fix

* fix ups

* Add tests

* Address PR feedback

* Pass metrics remote as a prop
This commit is contained in:
Darnell Andries
2026-05-28 11:37:29 -07:00
committed by GitHub
parent e6ea5cd2f2
commit 075088f2a4
26 changed files with 460 additions and 16 deletions
+2 -1
View File
@@ -643,7 +643,8 @@ void BraveContentBrowserClient::RegisterTrustedWebUIInterfaceBrokers(
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
if (email_aliases::features::IsEmailAliasesEnabled()) {
registry.ForWebUI<BraveSettingsUI>()
.Add<email_aliases::mojom::EmailAliasesService>();
.Add<email_aliases::mojom::EmailAliasesService>()
.Add<email_aliases::mojom::EmailAliasesMetrics>();
}
#endif
if (brave_account::features::IsBraveAccountEnabled()) {
@@ -774,7 +774,8 @@ bool BraveBrowserCommandController::ExecuteBraveCommandWithDisposition(
break;
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
case IDC_SHOW_EMAIL_ALIASES:
browser_->GetFeatures().email_aliases_controller()->OpenSettingsPage();
browser_->GetFeatures().email_aliases_controller()->OpenSettingsPage(
email_aliases::SettingsPageMethod::kAppMenu);
break;
#endif
#if BUILDFLAG(ENABLE_CONTAINERS)
@@ -6,10 +6,12 @@
#include "brave/browser/ui/email_aliases/email_aliases_controller.h"
#include <memory>
#include <optional>
#include "base/check_is_test.h"
#include "brave/browser/ui/webui/email_aliases/email_aliases_panel_ui.h"
#include "brave/components/constants/webui_url_constants.h"
#include "brave/components/email_aliases/constants.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
@@ -24,7 +26,6 @@
namespace {
inline constexpr char kEmailAliasesPanelURL[] = "chrome://email-aliases.panel/";
constexpr char kEmailAliasesSettingsURL[] = "brave://settings/email-aliases";
constexpr int kDialogWidth = 512;
constexpr gfx::Size kDialogMinSize(kDialogWidth, 336);
constexpr gfx::Size kDialogMaxSize(kDialogWidth, 794);
@@ -162,11 +163,13 @@ bool EmailAliasesController::IsAvailableFor(
params.form_control_type == FormControlType::kInputText;
}
void EmailAliasesController::ShowBubble(content::WebContents* initiator,
content::RenderFrameHost* render_frame,
uint64_t field_renderer_id) {
void EmailAliasesController::ShowBubble(
content::WebContents* initiator,
content::RenderFrameHost* render_frame,
uint64_t field_renderer_id,
std::optional<SettingsPageMethod> method) {
if (!email_aliases_service_->IsAuthenticated()) {
return OpenSettingsPage();
return OpenSettingsPage(method);
}
CloseBubble();
@@ -186,7 +189,11 @@ void EmailAliasesController::CloseBubble() {
bubble_->GetWebDialogDelegate()->OnDialogClosed({});
}
void EmailAliasesController::OpenSettingsPage() {
void EmailAliasesController::OpenSettingsPage(
std::optional<SettingsPageMethod> method) {
if (method) {
email_aliases_service_->metrics().RecordSettingsPageNavigation(*method);
}
ShowSingletonTabOverwritingNTP(browser_view_->browser(),
GURL(kEmailAliasesSettingsURL));
}
@@ -6,7 +6,10 @@
#ifndef BRAVE_BROWSER_UI_EMAIL_ALIASES_EMAIL_ALIASES_CONTROLLER_H_
#define BRAVE_BROWSER_UI_EMAIL_ALIASES_EMAIL_ALIASES_CONTROLLER_H_
#include <optional>
#include "base/memory/raw_ptr.h"
#include "brave/components/email_aliases/email_aliases_metrics.h"
#include "brave/components/email_aliases/email_aliases_service.h"
class BrowserView;
@@ -33,9 +36,11 @@ class EmailAliasesController {
void ShowBubble(content::WebContents* initiator,
content::RenderFrameHost* render_frame,
uint64_t field_renderer_id);
uint64_t field_renderer_id,
std::optional<SettingsPageMethod> method = std::nullopt);
void CloseBubble();
void OpenSettingsPage();
void OpenSettingsPage(
std::optional<SettingsPageMethod> method = std::nullopt);
content::WebContents* GetBubbleForTesting();
static void DisableAutoCloseBubbleForTesting(bool disale_autoclose);
@@ -36,6 +36,14 @@
#include "brave/components/ai_chat/core/browser/ai_chat_metrics.h"
#endif
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
#include "brave/browser/email_aliases/email_aliases_service_factory.h"
#include "brave/components/email_aliases/constants.h"
#include "brave/components/email_aliases/email_aliases_metrics.h"
#include "brave/components/email_aliases/email_aliases_service.h"
#include "brave/components/email_aliases/features.h"
#endif
namespace {
using brave_search_conversion::ConversionType;
@@ -106,6 +114,17 @@ BraveOmniboxClientImpl::BraveOmniboxClientImpl(LocationBar* location_bar,
}
}
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
if (email_aliases::features::IsEmailAliasesEnabled()) {
auto* email_aliases_service =
email_aliases::EmailAliasesServiceFactory::GetServiceForProfile(
profile);
if (email_aliases_service) {
email_aliases_metrics_ = &email_aliases_service->metrics();
}
}
#endif
pref_change_registrar_.Init(profile_->GetPrefs());
pref_change_registrar_.Add(
brave_rewards::prefs::kEnabled,
@@ -191,6 +210,14 @@ void BraveOmniboxClientImpl::OnAutocompleteAccept(
break;
}
}
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
if (email_aliases_metrics_ &&
match.type == AutocompleteMatchType::URL_WHAT_YOU_TYPED &&
destination_url.spec() == email_aliases::kEmailAliasesSettingsURL) {
email_aliases_metrics_->RecordSettingsPageNavigation(
email_aliases::SettingsPageMethod::kManualNavigation);
}
#endif
ChromeOmniboxClient::OnAutocompleteAccept(
destination_url, post_content, disposition, transition, match_type,
@@ -8,6 +8,7 @@
#include "base/memory/raw_ptr.h"
#include "brave/browser/autocomplete/brave_autocomplete_scheme_classifier.h"
#include "brave/components/email_aliases/buildflags/buildflags.h"
#include "brave/components/time_period_storage/weekly_storage.h"
#include "chrome/browser/ui/omnibox/chrome_omnibox_client.h"
#include "components/prefs/pref_change_registrar.h"
@@ -20,6 +21,10 @@ namespace ai_chat {
class AIChatMetrics;
} // namespace ai_chat
namespace email_aliases {
class EmailAliasesMetrics;
} // namespace email_aliases
namespace misc_metrics {
class BraveSearchMetrics;
class NavigationSourceMetrics;
@@ -62,6 +67,9 @@ class BraveOmniboxClientImpl : public ChromeOmniboxClient {
raw_ptr<misc_metrics::BraveSearchMetrics> brave_search_metrics_ = nullptr;
raw_ptr<misc_metrics::NavigationSourceMetrics> navigation_source_metrics_ =
nullptr;
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
raw_ptr<email_aliases::EmailAliasesMetrics> email_aliases_metrics_ = nullptr;
#endif
BraveAutocompleteSchemeClassifier scheme_classifier_;
WeeklyStorage search_storage_;
PrefChangeRegistrar pref_change_registrar_;
+11
View File
@@ -134,6 +134,7 @@
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
#include "brave/browser/email_aliases/email_aliases_service_factory.h"
#include "brave/components/email_aliases/email_aliases.mojom.h"
#include "brave/components/email_aliases/email_aliases_service.h"
#include "brave/components/email_aliases/features.h"
#endif
@@ -413,6 +414,16 @@ void BraveSettingsUI::BindInterface(
email_aliases::EmailAliasesServiceFactory::BindForProfile(
profile, std::move(receiver));
}
void BraveSettingsUI::BindInterface(
mojo::PendingReceiver<email_aliases::mojom::EmailAliasesMetrics> receiver) {
auto* profile = Profile::FromWebUI(web_ui());
auto* service =
email_aliases::EmailAliasesServiceFactory::GetServiceForProfile(profile);
if (service) {
service->metrics().BindInterface(std::move(receiver));
}
}
#endif // BUILDFLAG(ENABLE_EMAIL_ALIASES)
void BraveSettingsUI::BindInterface(
+3
View File
@@ -90,6 +90,9 @@ class BraveSettingsUI : public settings::SettingsUI {
void BindInterface(
mojo::PendingReceiver<email_aliases::mojom::EmailAliasesService>
pending_receiver);
void BindInterface(
mojo::PendingReceiver<email_aliases::mojom::EmailAliasesMetrics>
pending_receiver);
#endif
void BindInterface(
@@ -455,8 +455,10 @@ void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) {
#if BUILDFLAG(ENABLE_EMAIL_ALIASES)
case IDC_NEW_EMAIL_ALIAS:
if (auto* email_aliases = GetEmailAliasesController(GetBrowser())) {
email_aliases->ShowBubble(source_web_contents_, GetRenderFrameHost(),
params_.field_renderer_id);
email_aliases->ShowBubble(
source_web_contents_, GetRenderFrameHost(),
params_.field_renderer_id,
email_aliases::SettingsPageMethod::kContextMenu);
}
break;
#endif
@@ -164,8 +164,9 @@ class BraveChromeAutofillClient : public ChromeAutofillClient {
auto* render_frame_host = autofill::FindRenderFrameHostByToken(
*web_contents(), field.frame_token);
if (render_frame_host) {
email_aliases->ShowBubble(web_contents(), render_frame_host,
field.renderer_id.value());
email_aliases->ShowBubble(
web_contents(), render_frame_host, field.renderer_id.value(),
email_aliases::SettingsPageMethod::kAutofillBubble);
}
}
return true;
+6
View File
@@ -47,9 +47,12 @@ static_library("features") {
static_library("service") {
sources = [
"constants.h",
"email_aliases_auth.cc",
"email_aliases_auth.h",
"email_aliases_endpoints.h",
"email_aliases_metrics.cc",
"email_aliases_metrics.h",
"email_aliases_notes.cc",
"email_aliases_notes.h",
"email_aliases_service.cc",
@@ -67,7 +70,9 @@ static_library("service") {
"//brave/components/brave_account/mojom",
"//brave/components/constants",
"//brave/components/email_aliases:features",
"//brave/components/p3a_utils",
"//brave/components/resources:strings_grit",
"//brave/components/time_period_storage",
"//components/keyed_service/core",
"//components/pref_registry",
"//components/prefs",
@@ -101,6 +106,7 @@ source_set("test_utils") {
source_set("unit_tests") {
testonly = true
sources = [
"email_aliases_metrics_unittest.cc",
"email_aliases_notes_unittest.cc",
"email_aliases_service_unittest.cc",
]
+16
View File
@@ -0,0 +1,16 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_EMAIL_ALIASES_CONSTANTS_H_
#define BRAVE_COMPONENTS_EMAIL_ALIASES_CONSTANTS_H_
namespace email_aliases {
inline constexpr char kEmailAliasesSettingsURL[] =
"brave://settings/email-aliases";
} // namespace email_aliases
#endif // BRAVE_COMPONENTS_EMAIL_ALIASES_CONSTANTS_H_
@@ -71,6 +71,12 @@ interface EmailAliasesService {
AddObserver(pending_remote<EmailAliasesServiceObserver> observer);
};
// Interface for reporting email aliases metrics.
interface EmailAliasesMetrics {
// Record that the user copied an alias to the clipboard.
OnAliasCopied();
};
// Interface for handling `Generate new alias` dialog actions
// on the browser side.
interface EmailAliasesPanelHandler {
@@ -0,0 +1,109 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/email_aliases/email_aliases_metrics.h"
#include <limits>
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "brave/components/email_aliases/email_aliases_notes.h"
#include "brave/components/email_aliases/pref_names.h"
#include "brave/components/p3a_utils/bucket.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace email_aliases {
namespace {
constexpr int kCountBuckets[] = {0, 5, 15};
} // namespace
EmailAliasesMetrics::EmailAliasesMetrics(PrefService& pref_service)
: pref_service_(pref_service),
clipboard_copy_storage_(&pref_service,
prefs::kClipboardCopyCountStorage) {
pref_change_registrar_.Init(&pref_service);
pref_change_registrar_.Add(
prefs::kEmailAliasesNotes,
base::BindRepeating(&EmailAliasesMetrics::ReportNotesCount,
base::Unretained(this)));
ReportAllMetrics();
}
EmailAliasesMetrics::~EmailAliasesMetrics() = default;
// static
void EmailAliasesMetrics::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kAliasesPresent, false);
registry->RegisterListPref(prefs::kClipboardCopyCountStorage);
registry->RegisterBooleanPref(prefs::kSettingsPageMethodReported, false);
}
void EmailAliasesMetrics::RecordSettingsPageNavigation(
SettingsPageMethod method) {
if (pref_service_->GetBoolean(prefs::kSettingsPageMethodReported)) {
return;
}
UMA_HISTOGRAM_ENUMERATION(kSettingsPageMethodHistogramName, method);
pref_service_->SetBoolean(prefs::kSettingsPageMethodReported, true);
}
void EmailAliasesMetrics::BindInterface(
mojo::PendingReceiver<mojom::EmailAliasesMetrics> receiver) {
receivers_.Add(this, std::move(receiver));
}
void EmailAliasesMetrics::ReportEmailAliasPresence(bool is_present) {
pref_service_->SetBoolean(prefs::kAliasesPresent, is_present);
if (!is_present) {
return;
}
// Suppress the settings page navigation metric for existing users who already
// have aliases, to avoid attributing prior opt-ins.
if (!pref_service_->GetBoolean(prefs::kSettingsPageMethodReported)) {
pref_service_->SetBoolean(prefs::kSettingsPageMethodReported, true);
}
ReportAllMetrics();
}
void EmailAliasesMetrics::OnAliasCopied() {
clipboard_copy_storage_.AddDelta(1u);
ReportCopyCount();
}
void EmailAliasesMetrics::ReportAllMetrics() {
report_timer_.Start(FROM_HERE, base::Time::Now() + base::Days(1),
base::BindOnce(&EmailAliasesMetrics::ReportAllMetrics,
base::Unretained(this)));
ReportCopyCount();
ReportNotesCount();
}
void EmailAliasesMetrics::ReportCopyCount() {
uint64_t total = clipboard_copy_storage_.GetWeeklySum();
if (total == 0 ||
total > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
return;
}
p3a_utils::RecordToHistogramBucket(kClipboardCopyCountHistogramName,
kCountBuckets, static_cast<int>(total));
}
void EmailAliasesMetrics::ReportNotesCount() {
if (!pref_service_->GetBoolean(prefs::kAliasesPresent)) {
return;
}
size_t count = EmailAliasesNotes::GetTotalCount(*pref_service_);
if (count > static_cast<size_t>(std::numeric_limits<int>::max())) {
return;
}
p3a_utils::RecordToHistogramBucket(kNotesCountHistogramName, kCountBuckets,
static_cast<int>(count));
}
} // namespace email_aliases
@@ -0,0 +1,70 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_EMAIL_ALIASES_EMAIL_ALIASES_METRICS_H_
#define BRAVE_COMPONENTS_EMAIL_ALIASES_EMAIL_ALIASES_METRICS_H_
#include "base/memory/raw_ref.h"
#include "base/timer/wall_clock_timer.h"
#include "brave/components/email_aliases/email_aliases.mojom.h"
#include "brave/components/time_period_storage/weekly_storage.h"
#include "components/prefs/pref_change_registrar.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
class PrefRegistrySimple;
class PrefService;
namespace email_aliases {
inline constexpr char kClipboardCopyCountHistogramName[] =
"Brave.EmailAliases.ClipboardCopyCount";
inline constexpr char kNotesCountHistogramName[] =
"Brave.EmailAliases.NotesCount";
inline constexpr char kSettingsPageMethodHistogramName[] =
"Brave.EmailAliases.SettingsPageMethod";
enum class SettingsPageMethod {
kContextMenu = 0,
kAutofillBubble = 1,
kAppMenu = 2,
kManualNavigation = 3,
kMaxValue = kManualNavigation,
};
class EmailAliasesMetrics : public mojom::EmailAliasesMetrics {
public:
explicit EmailAliasesMetrics(PrefService& pref_service);
~EmailAliasesMetrics() override;
EmailAliasesMetrics(const EmailAliasesMetrics&) = delete;
EmailAliasesMetrics& operator=(const EmailAliasesMetrics&) = delete;
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
void BindInterface(
mojo::PendingReceiver<mojom::EmailAliasesMetrics> receiver);
// mojom::EmailAliasesMetrics:
void OnAliasCopied() override;
void RecordSettingsPageNavigation(SettingsPageMethod method);
void ReportEmailAliasPresence(bool is_present);
private:
void ReportAllMetrics();
void ReportCopyCount();
void ReportNotesCount();
const raw_ref<PrefService> pref_service_;
WeeklyStorage clipboard_copy_storage_;
PrefChangeRegistrar pref_change_registrar_;
base::WallClockTimer report_timer_;
mojo::ReceiverSet<mojom::EmailAliasesMetrics> receivers_;
};
} // namespace email_aliases
#endif // BRAVE_COMPONENTS_EMAIL_ALIASES_EMAIL_ALIASES_METRICS_H_
@@ -0,0 +1,108 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/email_aliases/email_aliases_metrics.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "brave/components/email_aliases/email_aliases_notes.h"
#include "brave/components/email_aliases/pref_names.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace email_aliases {
class EmailAliasesMetricsTest : public testing::Test {
public:
EmailAliasesMetricsTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
EmailAliasesMetrics::RegisterProfilePrefs(pref_service_.registry());
EmailAliasesNotes::RegisterProfilePrefs(pref_service_.registry());
metrics_ = std::make_unique<EmailAliasesMetrics>(pref_service_);
}
protected:
base::test::TaskEnvironment task_environment_;
TestingPrefServiceSimple pref_service_;
base::HistogramTester histogram_tester_;
std::unique_ptr<EmailAliasesMetrics> metrics_;
};
TEST_F(EmailAliasesMetricsTest, NotesCount) {
// With aliases present and no notes, reports bucket 0.
metrics_->ReportEmailAliasPresence(true);
histogram_tester_.ExpectUniqueSample(kNotesCountHistogramName, 0, 1);
// 1 note = bucket 1 (1-5).
EmailAliasesNotes notes(pref_service_, "user@example.com");
notes.UpdateNote("alias@example.com", "note");
histogram_tester_.ExpectBucketCount(kNotesCountHistogramName, 1, 1);
// 6 notes = bucket 2 (6-15).
for (int i = 1; i <= 5; i++) {
notes.UpdateNote("alias" + base::NumberToString(i) + "@example.com",
"note");
}
histogram_tester_.ExpectBucketCount(kNotesCountHistogramName, 2, 1);
// Without aliases present, further note changes should not report.
metrics_->ReportEmailAliasPresence(false);
histogram_tester_.ExpectTotalCount(kNotesCountHistogramName, 7);
notes.UpdateNote("alias100@example.com", "note");
histogram_tester_.ExpectTotalCount(kNotesCountHistogramName, 7);
}
TEST_F(EmailAliasesMetricsTest, CopyCount) {
metrics_->ReportEmailAliasPresence(true);
histogram_tester_.ExpectTotalCount(kClipboardCopyCountHistogramName, 0);
// 3 copies = bucket 1 (1-5). Running sum recorded each call:
// call 1: sum=1 -> bucket 1, call 2: sum=2 -> bucket 1, call 3: sum=3 ->
// bucket 1.
metrics_->OnAliasCopied();
metrics_->OnAliasCopied();
metrics_->OnAliasCopied();
histogram_tester_.ExpectUniqueSample(kClipboardCopyCountHistogramName, 1, 3);
// 6 more copies pushes sum to 9 = bucket 2 (6-15).
for (int i = 0; i < 6; i++) {
metrics_->OnAliasCopied();
}
histogram_tester_.ExpectBucketCount(kClipboardCopyCountHistogramName, 2, 4);
// Timer fires daily for 14 days — 7 additional reports while in weekly
// window.
task_environment_.FastForwardBy(base::Days(14));
histogram_tester_.ExpectTotalCount(kClipboardCopyCountHistogramName, 15);
}
TEST_F(EmailAliasesMetricsTest, SettingsPageNavigation) {
metrics_->RecordSettingsPageNavigation(SettingsPageMethod::kManualNavigation);
histogram_tester_.ExpectUniqueSample(kSettingsPageMethodHistogramName,
SettingsPageMethod::kManualNavigation,
1);
// Second call and existing users with aliases should be suppressed.
metrics_->RecordSettingsPageNavigation(SettingsPageMethod::kAppMenu);
histogram_tester_.ExpectTotalCount(kSettingsPageMethodHistogramName, 1);
metrics_->ReportEmailAliasPresence(true);
metrics_->RecordSettingsPageNavigation(SettingsPageMethod::kAppMenu);
histogram_tester_.ExpectTotalCount(kSettingsPageMethodHistogramName, 1);
}
TEST_F(EmailAliasesMetricsTest, ReportAllMetricsOnTimer) {
metrics_->ReportEmailAliasPresence(true);
histogram_tester_.ExpectTotalCount(kNotesCountHistogramName, 1);
task_environment_.FastForwardBy(base::Days(1));
histogram_tester_.ExpectTotalCount(kNotesCountHistogramName, 2);
}
} // namespace email_aliases
@@ -5,6 +5,8 @@
#include "brave/components/email_aliases/email_aliases_notes.h"
#include <numeric>
#include "brave/components/email_aliases/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
@@ -26,6 +28,16 @@ void EmailAliasesNotes::RegisterProfilePrefs(PrefRegistrySimple* registry) {
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
}
// static
size_t EmailAliasesNotes::GetTotalCount(PrefService& pref_service) {
const auto& pref = pref_service.GetDict(prefs::kEmailAliasesNotes);
return std::reduce(
pref.begin(), pref.end(), size_t{0}, [](size_t acc, const auto& entry) {
return acc +
(entry.second.is_dict() ? entry.second.GetDict().size() : 0);
});
}
std::optional<std::string> EmailAliasesNotes::GetNote(
const std::string& alias) {
const auto& pref = pref_service_->GetDict(prefs::kEmailAliasesNotes);
@@ -26,6 +26,7 @@ class EmailAliasesNotes {
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
static size_t GetTotalCount(PrefService& pref_service);
std::optional<std::string> GetNote(const std::string& alias);
void UpdateNote(const std::string& alias, const std::string& notes);
void RemoveNote(const std::string& alias);
@@ -18,6 +18,7 @@
#include "brave/components/constants/network_constants.h"
#include "brave/components/email_aliases/email_aliases.mojom.h"
#include "brave/components/email_aliases/email_aliases_api.h"
#include "brave/components/email_aliases/email_aliases_metrics.h"
#include "brave/components/email_aliases/email_aliases_notes.h"
#include "brave/components/email_aliases/features.h"
#include "brave/components/email_aliases/pref_names.h"
@@ -104,7 +105,9 @@ EmailAliasesService::EmailAliasesService(
brave_account_auth,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService& pref_service)
: url_loader_factory_(url_loader_factory), pref_service_(pref_service) {
: url_loader_factory_(url_loader_factory),
pref_service_(pref_service),
metrics_(pref_service) {
CHECK(base::FeatureList::IsEnabled(email_aliases::features::kEmailAliases));
CHECK(brave_account_auth);
@@ -118,6 +121,7 @@ EmailAliasesService::~EmailAliasesService() = default;
// static
void EmailAliasesService::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kEmailAliasesEnabled, true);
EmailAliasesMetrics::RegisterProfilePrefs(registry);
EmailAliasesNotes::RegisterProfilePrefs(registry);
}
@@ -211,6 +215,9 @@ void EmailAliasesService::OnGenerateAliasResponse(
parsed.has_value()
? base::expected<std::string, std::string>(parsed.value().alias)
: base::unexpected(parsed.error());
if (result.has_value()) {
metrics_.ReportEmailAliasPresence(true);
}
std::move(user_callback).Run(std::move(result));
}
@@ -359,6 +366,7 @@ void EmailAliasesService::OnRefreshAliasesResponse(
aliases.push_back(std::move(alias_obj));
}
metrics_.ReportEmailAliasPresence(!aliases.empty());
NotifyObserversAliasesUpdated(
observers_, mojom::AliasesUpdate::NewAliases(std::move(aliases)));
}
@@ -15,6 +15,7 @@
#include "brave/components/email_aliases/email_aliases.mojom.h"
#include "brave/components/email_aliases/email_aliases_auth.h"
#include "brave/components/email_aliases/email_aliases_endpoints.h"
#include "brave/components/email_aliases/email_aliases_metrics.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@@ -82,6 +83,8 @@ class EmailAliasesService : public KeyedService,
EmailAliasesAuth* GetAuth();
EmailAliasesMetrics& metrics() { return metrics_; }
private:
using TokenResult =
base::expected<brave_account::mojom::GetServiceTokenResultPtr,
@@ -134,6 +137,8 @@ class EmailAliasesService : public KeyedService,
const raw_ref<PrefService> pref_service_;
EmailAliasesMetrics metrics_;
// WeakPtrFactory to safely bind callbacks across async network operations.
base::WeakPtrFactory<EmailAliasesService> weak_factory_{this};
};
+11
View File
@@ -13,6 +13,17 @@ inline constexpr char kEmailAliasesEnabled[] = "brave.email_aliases.enabled";
// Preference key for storing the aliases notes.
inline constexpr char kEmailAliasesNotes[] = "brave.email_aliases.notes";
// Set to true once the user has at least one alias.
inline constexpr char kAliasesPresent[] = "brave.email_aliases.aliases_present";
// Weekly storage list for clipboard copy count metric.
inline constexpr char kClipboardCopyCountStorage[] =
"brave.email_aliases.clipboard_copy_count_storage";
// Set to true once the settings page access method metric has been recorded.
inline constexpr char kSettingsPageMethodReported[] =
"brave.email_aliases.settings_page_method_reported";
} // namespace email_aliases::prefs
#endif // BRAVE_COMPONENTS_EMAIL_ALIASES_PREF_NAMES_H_
@@ -70,15 +70,18 @@ const AliasMenuItem = ({
const CopyToast = ({
text,
tabIndex,
onCopy,
children,
}: {
text: string
tabIndex?: number
onCopy?: () => void
children: React.ReactNode
}) => {
const [copied, setCopied] = React.useState<boolean>(false)
const copy = () => {
navigator.clipboard.writeText(text)
onCopy?.()
setCopied(true)
setTimeout(() => setCopied(false), 1000)
}
@@ -105,16 +108,21 @@ const CopyToast = ({
export const AliasItem = ({
alias,
onCopy,
onEdit,
onDelete,
}: {
alias: Alias
onCopy: () => void
onEdit: () => void
onDelete: () => void
}) => (
<AliasItemRow>
<Col>
<CopyToast text={alias.email}>
<CopyToast
text={alias.email}
onCopy={onCopy}
>
<EmailContainer
title={getLocale(S.SETTINGS_EMAIL_ALIASES_CLICK_TO_COPY_ALIAS)}
>
@@ -136,6 +144,7 @@ export const AliasItem = ({
<CopyToast
text={alias.email}
tabIndex={0}
onCopy={onCopy}
>
<Button
fab
@@ -20,6 +20,7 @@ import Row from './styles/Row'
import styled from 'styled-components'
import {
Alias,
EmailAliasesMetricsRemote,
EmailAliasesServiceInterface,
MAX_ALIASES,
} from 'gen/brave/components/email_aliases/email_aliases.mojom.m'
@@ -70,10 +71,12 @@ export const AliasList = ({
aliases,
authEmail,
emailAliasesService,
metrics,
}: {
emailAliasesService: EmailAliasesServiceInterface
aliases: Alias[]
authEmail: string
metrics?: EmailAliasesMetricsRemote
}) => {
const [editState, setEditState] = React.useState<EditState>({ mode: 'None' })
return (
@@ -84,6 +87,7 @@ export const AliasList = ({
<AliasItem
key={alias.email}
alias={alias}
onCopy={() => metrics?.onAliasCopied()}
onEdit={() => setEditState({ mode: 'Edit', alias: alias })}
onDelete={() => setEditState({ mode: 'Delete', alias: alias })}
></AliasItem>
@@ -14,6 +14,7 @@ import {
AliasesUpdate,
AuthState,
AuthenticationStatus,
EmailAliasesMetricsRemote,
EmailAliasesServiceInterface,
} from 'gen/brave/components/email_aliases/email_aliases.mojom.m'
@@ -45,10 +46,12 @@ export const ManagePage = ({
aliasesUpdate,
authState,
emailAliasesService,
metrics,
}: {
aliasesUpdate: AliasesUpdate
authState: AuthState
emailAliasesService: EmailAliasesServiceInterface
metrics?: EmailAliasesMetricsRemote
}) => (
<PageCol>
{authState.status === AuthenticationStatus.kAuthenticated
@@ -59,6 +62,7 @@ export const ManagePage = ({
aliases={aliasesUpdate.aliases!}
authEmail={authState.email}
emailAliasesService={emailAliasesService}
metrics={metrics}
/>
))}
</PageCol>
@@ -9,6 +9,7 @@ import { StyleSheetManager } from 'styled-components'
import * as React from 'react'
import { setIconBasePath } from '@brave/leo/react/icon'
import {
EmailAliasesMetrics,
EmailAliasesServiceInterface,
EmailAliasesServiceObserverInterface,
EmailAliasesServiceObserverReceiver,
@@ -19,9 +20,11 @@ import { useEmailAliases } from './content/use_email_aliases'
export const ManagePageConnected = ({
emailAliasesService,
bindObserver,
metrics,
}: {
emailAliasesService: EmailAliasesServiceInterface
bindObserver: (observer: EmailAliasesServiceObserverInterface) => () => void
metrics?: ReturnType<typeof EmailAliasesMetrics.getRemote>
}) => {
const { authState, aliasesUpdate } = useEmailAliases(bindObserver)
return (
@@ -29,6 +32,7 @@ export const ManagePageConnected = ({
authState={authState}
aliasesUpdate={aliasesUpdate}
emailAliasesService={emailAliasesService}
metrics={metrics}
/>
)
}
@@ -37,6 +41,7 @@ export const mount = (signInElem: HTMLElement, manageElem: HTMLElement) => {
setIconBasePath('//resources/brave-icons')
const emailAliasesService = EmailAliasesService.getRemote()
const emailAliasesMetrics = EmailAliasesMetrics.getRemote()
const bindObserver = (observer: EmailAliasesServiceObserverInterface) => {
const observerReceiver = new EmailAliasesServiceObserverReceiver(observer)
@@ -60,6 +65,7 @@ export const mount = (signInElem: HTMLElement, manageElem: HTMLElement) => {
<ManagePageConnected
emailAliasesService={emailAliasesService}
bindObserver={bindObserver}
metrics={emailAliasesMetrics}
/>
</StyleSheetManager>,
)
+3
View File
@@ -90,6 +90,9 @@ inline constexpr auto kCollectedTypicalHistograms =
{"Brave.DNS.AutoSecureRequests.Quad9.2", MetricConfig{.ephemeral = true}},
{"Brave.DNS.AutoSecureRequests.Wikimedia.2", MetricConfig{.ephemeral = true}},
{"Brave.DNS.SecureSetting", {}},
{"Brave.EmailAliases.ClipboardCopyCount", MetricConfig{.ephemeral = true}},
{"Brave.EmailAliases.NotesCount", MetricConfig{.ephemeral = true}},
{"Brave.EmailAliases.SettingsPageMethod", MetricConfig{.ephemeral = true}},
{"Brave.Extensions.AdBlock", {}},
{"Brave.Extensions.SelectManifestV2", {}},
{"Brave.IOS.IsLikelyDefault", MetricConfig{