diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:33 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:33 +0000 |
commit | 086c044dc34dfc0f74fbe41f4ecb402b2cd34884 (patch) | |
tree | a4f824bd33cb075dd5aa3eb5a0a94af221bbe83a /toolkit/components/resistfingerprinting | |
parent | Adding debian version 124.0.1-1. (diff) | |
download | firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.tar.xz firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/resistfingerprinting')
9 files changed, 923 insertions, 1 deletions
diff --git a/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs b/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs index a085aa492a..223c0259b7 100644 --- a/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs +++ b/toolkit/components/resistfingerprinting/RFPHelper.sys.mjs @@ -20,7 +20,7 @@ var logConsole; function log(msg) { if (!logConsole) { logConsole = console.createInstance({ - prefix: "RFPHelper.jsm", + prefix: "RFPHelper", maxLogLevelPref: "privacy.resistFingerprinting.jsmloglevel", }); } diff --git a/toolkit/components/resistfingerprinting/metrics.yaml b/toolkit/components/resistfingerprinting/metrics.yaml index 916c8c7871..3c706d20fa 100644 --- a/toolkit/components/resistfingerprinting/metrics.yaml +++ b/toolkit/components/resistfingerprinting/metrics.yaml @@ -25,3 +25,387 @@ fingerprinting.protection: - tschuster@mozilla.com expires: never telemetry_mirror: FINGERPRINTING_PROTECTION_CANVAS_NOISE_CALCULATE_TIME_MS + + +characteristics: + client_identifier: + type: uuid + description: > + A unique identifier for a user, not the same as the normal Telemetry + client_id, but needed so we can deduplicate reports and only take the most + recent one per user. + lifetime: application + send_in_pings: + - user-characteristics + - deletion-request + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879154 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879154#c8 + expires: never + data_sensitivity: + - technical + + submission_schema: + type: quantity + unit: versions + description: > + An incrementing constant that represents the current schema/source of the + data present in a ping. By referring to this value in a ping, one can know + for certain the provenance of other data present in the ping, and what + data may or may not be present. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879154 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879154#c8 + expires: never + + max_touch_points: + type: quantity + unit: Fingers + description: > + The number of touch points we will report to the web. On Android, this is + based on Android's FEATURE_TOUCHSCREEN* constants - Mozilla caps this at 5 + as Android stops distinguishing between numbers greater than 5. On + Windows this comes from the SM_MAXIMUMTOUCHES System Metric. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879156 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879156#c4 + expires: never + data_sensitivity: + - technical + + video_dynamic_range: + type: boolean + description: > + What LookAndFeel(VideoDynamicRange) reports. Note that CSSVideoDynamicRange + has an additional dependency on Color Depth. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + prefers_reduced_transparency: + type: boolean + description: > + What LookAndFeel(PrefersReducedTransparency) reports. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + prefers_reduced_motion: + type: boolean + description: > + What LookAndFeel(PrefersReducedMotion) reports. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + prefers_contrast: + type: quantity + unit: enum StylePrefersContrast value + description: > + What Gecko_MediaFeatures_PrefersContrast reports for a ContentDocument + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + inverted_colors: + type: boolean + description: > + What LookAndFeel(InvertedColors) reports. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + color_scheme: + type: quantity + unit: enum mozilla::ColorScheme value + description: > + The Color Scheme used for Content, from ContentPrefs() Preference Sheet. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + color_gamut: + type: quantity + unit: enum dom::ScreenColorGamut value + description: > + The Color Gamut reported by CSS + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + color_depth: + type: quantity + unit: bits + description: > + The Color Depth reported by CSS + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879624#c4 + expires: never + data_sensitivity: + - technical + + missing_fonts: + type: text + description: > + If a Font List is available for the user's platform, this + string_list contains the fonts that are missing from the user's + computer. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1880561 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1880561#c6 + expires: never + data_sensitivity: + # Text metrics are _required_ to be web_activity or highly_sensitive, so even though this + # is more like 'technical' (per the Data Review), I'm marking highly sensitive. + - highly_sensitive + + screen_width: + type: quantity + unit: pixels + description: > + Width of the primary screen in pixels. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881749 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881749#c5 + expires: never + data_sensitivity: + - technical + + screen_height: + type: quantity + unit: pixels + description: > + Height of the primary screen in pixels. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881749 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881749#c5 + expires: never + data_sensitivity: + - technical + + processor_count: + type: quantity + unit: int + description: > + Number of processors. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881759 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881759#c4 + expires: never + data_sensitivity: + - technical + + timezone: + type: string + description: > + The the current timezone of the system + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881773 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881773#c4 + expires: never + data_sensitivity: + - interaction + + target_frame_rate: + type: quantity + unit: int + description: > + The target frame rate in frames-per-second. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882054 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882054#c3 + expires: never + data_sensitivity: + - technical + + prefs_intl_accept_languages: + type: string + description: > + Value of the intl.accept_languages pref. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882482 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882482#c6 + expires: never + data_sensitivity: + - interaction + + prefs_media_eme_enabled: + type: boolean + description: > + Value of the media.eme.enabled pref. + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882482 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882482#c6 + expires: never + data_sensitivity: + - interaction + + prefs_zoom_text_only: + type: boolean + description: > + Text-only zoom enabled (vs. full-zoom) + lifetime: application + send_in_pings: + - user-characteristics + notification_emails: + - tom@mozilla.com + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882482 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1882482#c6 + expires: never + data_sensitivity: + - interaction diff --git a/toolkit/components/resistfingerprinting/moz.build b/toolkit/components/resistfingerprinting/moz.build index 6ac25d3b76..9b4e9cc8ed 100644 --- a/toolkit/components/resistfingerprinting/moz.build +++ b/toolkit/components/resistfingerprinting/moz.build @@ -13,6 +13,17 @@ UNIFIED_SOURCES += [ "nsRFPService.cpp", "RelativeTimeline.cpp", ] +# Because nsUserCharacteristics doesn't `use namespace mozilla` (because we're going to wind +# up with a million includes on this file and pollution will get confusing), unified build +# will mask a lot of errors that would cause backouts. This exposes them locally. +SOURCES += [ + "nsUserCharacteristics.cpp", +] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": + LOCAL_INCLUDES += [ + "/xpcom/base", + ] SPHINX_TREES["resistfingerprinting"] = "docs" @@ -23,6 +34,7 @@ EXPORTS.mozilla += [ "RelativeTimeline.h", "RFPTargetIPCUtils.h", ] +EXPORTS.mozilla.gtest += ["nsUserCharacteristics.h"] EXTRA_JS_MODULES += [ "FingerprintingWebCompatService.sys.mjs", diff --git a/toolkit/components/resistfingerprinting/nsRFPService.cpp b/toolkit/components/resistfingerprinting/nsRFPService.cpp index 8579fe2a3b..643cc2cb7a 100644 --- a/toolkit/components/resistfingerprinting/nsRFPService.cpp +++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp @@ -71,6 +71,7 @@ #include "nsTLiteralString.h" #include "nsTPromiseFlatString.h" #include "nsTStringRepr.h" +#include "nsUserCharacteristics.h" #include "nsXPCOM.h" #include "nsICookieJarSettings.h" @@ -100,8 +101,14 @@ static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection"); #define RESIST_FINGERPRINTINGPROTECTION_OVERRIDE_PREF \ "privacy.fingerprintingProtection.overrides" +#define GLEAN_DATA_SUBMISSION_PREF "datareporting.healthreport.uploadEnabled" +#define USER_CHARACTERISTICS_UUID_PREF \ + "toolkit.telemetry.user_characteristics_ping.uuid" + #define RFP_TIMER_UNCONDITIONAL_VALUE 20 #define LAST_PB_SESSION_EXITED_TOPIC "last-pb-context-exited" +#define IDLE_TOPIC "browser-idle-startup-tasks-finished" +#define GFX_FEATURES "gfx-features-ready" static constexpr uint32_t kVideoFramesPerSec = 30; static constexpr uint32_t kVideoDroppedRatio = 5; @@ -156,6 +163,7 @@ already_AddRefed<nsRFPService> nsRFPService::GetOrCreate() { static const char* gCallbackPrefs[] = { RESIST_FINGERPRINTINGPROTECTION_OVERRIDE_PREF, + GLEAN_DATA_SUBMISSION_PREF, nullptr, }; @@ -176,6 +184,12 @@ nsresult nsRFPService::Init() { rv = obs->AddObserver(this, OBSERVER_TOPIC_IDLE_DAILY, false); NS_ENSURE_SUCCESS(rv, rv); + + rv = obs->AddObserver(this, IDLE_TOPIC, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = obs->AddObserver(this, GFX_FEATURES, false); + NS_ENSURE_SUCCESS(rv, rv); } Preferences::RegisterCallbacks(nsRFPService::PrefChanged, gCallbackPrefs, @@ -273,6 +287,8 @@ void nsRFPService::StartShutdown() { if (XRE_IsParentProcess()) { obs->RemoveObserver(this, LAST_PB_SESSION_EXITED_TOPIC); obs->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY); + obs->RemoveObserver(this, IDLE_TOPIC); + obs->RemoveObserver(this, GFX_FEATURES); } } @@ -290,16 +306,30 @@ void nsRFPService::PrefChanged(const char* aPref, void* aSelf) { } void nsRFPService::PrefChanged(const char* aPref) { + MOZ_LOG(gResistFingerprintingLog, LogLevel::Info, + ("Pref Changed: %s", aPref)); nsDependentCString pref(aPref); if (pref.EqualsLiteral(RESIST_FINGERPRINTINGPROTECTION_OVERRIDE_PREF)) { UpdateFPPOverrideList(); + } else if (pref.EqualsLiteral(GLEAN_DATA_SUBMISSION_PREF)) { + if (XRE_IsParentProcess() && + !Preferences::GetBool(GLEAN_DATA_SUBMISSION_PREF, false)) { + MOZ_LOG(gResistFingerprintingLog, LogLevel::Info, ("Clearing UUID")); + // If the user has unset the telemetry pref, wipe out the UUID pref value + // (The data will also be erased server-side via the "deletion-request" + // ping) + Preferences::SetCString(USER_CHARACTERISTICS_UUID_PREF, ""_ns); + } } } NS_IMETHODIMP nsRFPService::Observe(nsISupports* aObject, const char* aTopic, const char16_t* aMessage) { + const int kNumTopicsForUserCharacteristics = 2; + static int seenTopicsForUserCharacteristics = 0; + if (strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic) == 0) { StartShutdown(); } @@ -312,6 +342,14 @@ nsRFPService::Observe(nsISupports* aObject, const char* aTopic, ClearBrowsingSessionKey(pattern); } + if (!strcmp(IDLE_TOPIC, aTopic) || !strcmp(GFX_FEATURES, aTopic)) { + seenTopicsForUserCharacteristics++; + + if (seenTopicsForUserCharacteristics == kNumTopicsForUserCharacteristics) { + nsUserCharacteristics::MaybeSubmitPing(); + } + } + if (!strcmp(OBSERVER_TOPIC_IDLE_DAILY, aTopic)) { if (StaticPrefs:: privacy_resistFingerprinting_randomization_daily_reset_enabled()) { diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp new file mode 100644 index 0000000000..9bce616f81 --- /dev/null +++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#include "nsUserCharacteristics.h" + +#include "nsID.h" +#include "nsIUUIDGenerator.h" +#include "nsServiceManagerUtils.h" + +#include "mozilla/Logging.h" +#include "mozilla/glean/GleanPings.h" +#include "mozilla/glean/GleanMetrics.h" + +#include "mozilla/StaticPrefs_media.h" + +#include "mozilla/LookAndFeel.h" +#include "mozilla/PreferenceSheet.h" +#include "mozilla/RelativeLuminanceUtils.h" +#include "mozilla/ServoStyleConsts.h" +#include "mozilla/dom/ScreenBinding.h" +#include "mozilla/intl/TimeZone.h" +#include "mozilla/widget/ScreenManager.h" + +#include "gfxPlatformFontList.h" +#include "prsystem.h" +#if defined(XP_WIN) +# include "WinUtils.h" +#elif defined(MOZ_WIDGET_ANDROID) +# include "mozilla/java/GeckoAppShellWrappers.h" +#elif defined(XP_MACOSX) +# include "nsMacUtilsImpl.h" +#endif + +static mozilla::LazyLogModule gUserCharacteristicsLog("UserCharacteristics"); + +// ================================================================== +namespace testing { +extern "C" { + +int MaxTouchPoints() { +#if defined(XP_WIN) + return mozilla::widget::WinUtils::GetMaxTouchPoints(); +#elif defined(MOZ_WIDGET_ANDROID) + return mozilla::java::GeckoAppShell::GetMaxTouchPoints(); +#else + return 0; +#endif +} + +} // extern "C" +}; // namespace testing + +// ================================================================== +void PopulateCSSProperties() { + mozilla::glean::characteristics::video_dynamic_range.Set( + mozilla::LookAndFeel::GetInt( + mozilla::LookAndFeel::IntID::VideoDynamicRange)); + mozilla::glean::characteristics::prefers_reduced_transparency.Set( + mozilla::LookAndFeel::GetInt( + mozilla::LookAndFeel::IntID::PrefersReducedTransparency)); + mozilla::glean::characteristics::prefers_reduced_motion.Set( + mozilla::LookAndFeel::GetInt( + mozilla::LookAndFeel::IntID::PrefersReducedMotion)); + mozilla::glean::characteristics::inverted_colors.Set( + mozilla::LookAndFeel::GetInt( + mozilla::LookAndFeel::IntID::InvertedColors)); + mozilla::glean::characteristics::color_scheme.Set( + (int)mozilla::PreferenceSheet::ContentPrefs().mColorScheme); + + mozilla::StylePrefersContrast prefersContrast = [] { + // Replicates Gecko_MediaFeatures_PrefersContrast but without a Document + if (!mozilla::PreferenceSheet::ContentPrefs().mUseAccessibilityTheme && + mozilla::PreferenceSheet::ContentPrefs().mUseDocumentColors) { + return mozilla::StylePrefersContrast::NoPreference; + } + + const auto& colors = mozilla::PreferenceSheet::ContentPrefs().ColorsFor( + mozilla::ColorScheme::Light); + float ratio = mozilla::RelativeLuminanceUtils::ContrastRatio( + colors.mDefaultBackground, colors.mDefault); + // https://www.w3.org/TR/WCAG21/#contrast-minimum + if (ratio < 4.5f) { + return mozilla::StylePrefersContrast::Less; + } + // https://www.w3.org/TR/WCAG21/#contrast-enhanced + if (ratio >= 7.0f) { + return mozilla::StylePrefersContrast::More; + } + return mozilla::StylePrefersContrast::Custom; + }(); + mozilla::glean::characteristics::prefers_contrast.Set((int)prefersContrast); +} + +void PopulateScreenProperties() { + auto& screenManager = mozilla::widget::ScreenManager::GetSingleton(); + RefPtr<mozilla::widget::Screen> screen = screenManager.GetPrimaryScreen(); + MOZ_ASSERT(screen); + + mozilla::dom::ScreenColorGamut colorGamut; + screen->GetColorGamut(&colorGamut); + mozilla::glean::characteristics::color_gamut.Set((int)colorGamut); + + int32_t colorDepth; + screen->GetColorDepth(&colorDepth); + mozilla::glean::characteristics::color_depth.Set(colorDepth); + + mozilla::glean::characteristics::color_gamut.Set((int)colorGamut); + mozilla::glean::characteristics::color_depth.Set(colorDepth); + const mozilla::LayoutDeviceIntRect rect = screen->GetRect(); + mozilla::glean::characteristics::screen_height.Set(rect.Height()); + mozilla::glean::characteristics::screen_width.Set(rect.Width()); +} + +void PopulateMissingFonts() { + nsCString aMissingFonts; + gfxPlatformFontList::PlatformFontList()->GetMissingFonts(aMissingFonts); + + mozilla::glean::characteristics::missing_fonts.Set(aMissingFonts); +} + +void PopulatePrefs() { + nsAutoCString acceptLang; + mozilla::Preferences::GetLocalizedCString("intl.accept_languages", + acceptLang); + mozilla::glean::characteristics::prefs_intl_accept_languages.Set(acceptLang); + + mozilla::glean::characteristics::prefs_media_eme_enabled.Set( + mozilla::StaticPrefs::media_eme_enabled()); + + mozilla::glean::characteristics::prefs_zoom_text_only.Set( + !mozilla::Preferences::GetBool("browser.zoom.full")); +} + +// ================================================================== +// The current schema of the data. Anytime you add a metric, or change how a +// metric is set, this variable should be incremented. It'll be a lot. It's +// okay. We're going to need it to know (including during development) what is +// the source of the data we are looking at. +const int kSubmissionSchema = 0; + +/* static */ +void nsUserCharacteristics::MaybeSubmitPing() { + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug, + ("In MaybeSubmitPing()")); + MOZ_ASSERT(XRE_IsParentProcess()); + + /** + * There are two preferences at play here: + * - Last Version Sent - preference containing the last version sent by the + * user to Mozilla + * - Current Version - preference containing the version Mozilla would like + * the user to send + * + * A point of complexity arises in that these two values _may_ be changed + * by the user, even though neither is intended to be. + * + * When Current Version > Last Version Sent, we intend for the user to submit + * a new ping, which will include the schema version. Then update Last Version + * Sent = Current Version. + * + */ + const auto* const kLastVersionPref = + "toolkit.telemetry.user_characteristics_ping.last_version_sent"; + const auto* const kCurrentVersionPref = + "toolkit.telemetry.user_characteristics_ping.current_version"; + + auto lastSubmissionVersion = + mozilla::Preferences::GetInt(kLastVersionPref, 0); + auto currentVersion = mozilla::Preferences::GetInt(kCurrentVersionPref, 0); + + MOZ_ASSERT(currentVersion == -1 || lastSubmissionVersion <= currentVersion, + "lastSubmissionVersion is somehow greater than currentVersion " + "- did you edit prefs improperly?"); + + if (lastSubmissionVersion < 0) { + // This is a way for users to opt out of this ping specifically. + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug, + ("Returning, User Opt-out")); + return; + } + if (currentVersion == 0) { + // Do nothing. We do not want any pings. + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug, + ("Returning, currentVersion == 0")); + return; + } + if (currentVersion == -1) { + // currentVersion = -1 is a development value to force a ping submission + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Debug, + ("Force-Submitting Ping")); + if (NS_SUCCEEDED(PopulateData())) { + SubmitPing(); + } + return; + } + if (lastSubmissionVersion > currentVersion) { + // This is an unexpected scneario that indicates something is wrong. We + // asserted against it (in debug, above) We will try to sanity-correct + // ourselves by setting it to the current version. + mozilla::Preferences::SetInt(kLastVersionPref, currentVersion); + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning, + ("Returning, lastSubmissionVersion > currentVersion")); + return; + } + if (lastSubmissionVersion == currentVersion) { + // We are okay, we've already submitted the most recent ping + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning, + ("Returning, lastSubmissionVersion == currentVersion")); + return; + } + if (lastSubmissionVersion < currentVersion) { + if (NS_SUCCEEDED(PopulateData())) { + if (NS_SUCCEEDED(SubmitPing())) { + mozilla::Preferences::SetInt(kLastVersionPref, currentVersion); + } + } + } else { + MOZ_ASSERT_UNREACHABLE("Should never reach here"); + } +} + +const auto* const kUUIDPref = + "toolkit.telemetry.user_characteristics_ping.uuid"; + +/* static */ +nsresult nsUserCharacteristics::PopulateData(bool aTesting /* = false */) { + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning, + ("Populating Data")); + MOZ_ASSERT(XRE_IsParentProcess()); + mozilla::glean::characteristics::submission_schema.Set(kSubmissionSchema); + + nsAutoCString uuidString; + nsresult rv = mozilla::Preferences::GetCString(kUUIDPref, uuidString); + if (NS_FAILED(rv) || uuidString.Length() == 0) { + nsCOMPtr<nsIUUIDGenerator> uuidgen = + do_GetService("@mozilla.org/uuid-generator;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsIDToCString id(nsID::GenerateUUID()); + uuidString = id.get(); + mozilla::Preferences::SetCString(kUUIDPref, uuidString); + } + mozilla::glean::characteristics::client_identifier.Set(uuidString); + + mozilla::glean::characteristics::max_touch_points.Set( + testing::MaxTouchPoints()); + + if (aTesting) { + // Many of the later peices of data do not work in a gtest + // so just populate something, and return + return NS_OK; + } + + PopulateMissingFonts(); + PopulateCSSProperties(); + PopulateScreenProperties(); + PopulatePrefs(); + + mozilla::glean::characteristics::target_frame_rate.Set( + gfxPlatform::TargetFrameRate()); + + int32_t processorCount = 0; +#if defined(XP_MACOSX) + if (nsMacUtilsImpl::IsTCSMAvailable()) { + // On failure, zero is returned from GetPhysicalCPUCount() + // and we fallback to PR_GetNumberOfProcessors below. + processorCount = nsMacUtilsImpl::GetPhysicalCPUCount(); + } +#endif + if (processorCount == 0) { + processorCount = PR_GetNumberOfProcessors(); + } + mozilla::glean::characteristics::processor_count.Set(processorCount); + + AutoTArray<char16_t, 128> tzBuffer; + auto result = mozilla::intl::TimeZone::GetDefaultTimeZone(tzBuffer); + if (result.isOk()) { + NS_ConvertUTF16toUTF8 timeZone( + nsDependentString(tzBuffer.Elements(), tzBuffer.Length())); + mozilla::glean::characteristics::timezone.Set(timeZone); + } else { + mozilla::glean::characteristics::timezone.Set("<error>"_ns); + } + + return NS_OK; +} + +/* static */ +nsresult nsUserCharacteristics::SubmitPing() { + MOZ_LOG(gUserCharacteristicsLog, mozilla::LogLevel::Warning, + ("Submitting Ping")); + mozilla::glean_pings::UserCharacteristics.Submit(); + + return NS_OK; +} diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.h b/toolkit/components/resistfingerprinting/nsUserCharacteristics.h new file mode 100644 index 0000000000..a52bc9aea7 --- /dev/null +++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef __nsUserCharacteristics_h__ +#define __nsUserCharacteristics_h__ + +#include "ErrorList.h" + +class nsUserCharacteristics { + public: + static void MaybeSubmitPing(); + + // Public For testing + static nsresult PopulateData(bool aTesting = false); + static nsresult SubmitPing(); +}; + +namespace testing { +extern "C" { // Needed to call these in the gtest + +int MaxTouchPoints(); + +} // extern "C" +}; // namespace testing + +#endif /* __nsUserCharacteristics_h__ */ diff --git a/toolkit/components/resistfingerprinting/pings.yaml b/toolkit/components/resistfingerprinting/pings.yaml new file mode 100644 index 0000000000..46a4b2da19 --- /dev/null +++ b/toolkit/components/resistfingerprinting/pings.yaml @@ -0,0 +1,19 @@ +# 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 http://mozilla.org/MPL/2.0/. + +--- +$schema: moz://mozilla.org/schemas/glean/pings/2-0-0 + +user-characteristics: + description: | + A ping representing user hardware and software settings. Note that this + ping does not include client_id. More details are available in Bug 1879151 + include_client_id: false + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879151 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879154 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1879154#c8 + notification_emails: + - tritter@mozilla.com diff --git a/toolkit/components/resistfingerprinting/tests/gtest/moz.build b/toolkit/components/resistfingerprinting/tests/gtest/moz.build index dcc6a7e12e..a677def009 100644 --- a/toolkit/components/resistfingerprinting/tests/gtest/moz.build +++ b/toolkit/components/resistfingerprinting/tests/gtest/moz.build @@ -1,5 +1,6 @@ UNIFIED_SOURCES += [ "test_reduceprecision.cpp", + "test_usercharping.cpp", ] FINAL_LIBRARY = "xul-gtest" diff --git a/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp b/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp new file mode 100644 index 0000000000..dd1cbe7d46 --- /dev/null +++ b/toolkit/components/resistfingerprinting/tests/gtest/test_usercharping.cpp @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * 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 http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "mozilla/gtest/nsUserCharacteristics.h" + +#include "mozilla/glean/GleanPings.h" +#include "mozilla/glean/GleanMetrics.h" + +using namespace mozilla; + +const auto* const kUUIDPref = + "toolkit.telemetry.user_characteristics_ping.uuid"; + +TEST(ResistFingerprinting, UserCharacteristics_Simple) +{ + mozilla::glean::characteristics::max_touch_points.Set(7); + + bool submitted = false; + mozilla::glean_pings::UserCharacteristics.TestBeforeNextSubmit( + [&submitted](const nsACString& aReason) { + submitted = true; + + ASSERT_EQ( + 7, mozilla::glean::characteristics::max_touch_points.TestGetValue() + .unwrap() + .ref()); + }); + mozilla::glean_pings::UserCharacteristics.Submit(); + ASSERT_TRUE(submitted); +} + +TEST(ResistFingerprinting, UserCharacteristics_Complex) +{ + nsUserCharacteristics::PopulateData(true); + + bool submitted = false; + mozilla::glean_pings::UserCharacteristics.TestBeforeNextSubmit( + [&submitted](const nsACString& aReason) { + submitted = true; + + ASSERT_STRNE("", mozilla::glean::characteristics::client_identifier + .TestGetValue() + .unwrap() + .value() + .get()); + + nsCString fullUuidStr; + Preferences::GetCString(kUUIDPref, fullUuidStr); + + // Remove the '{' and '}' + nsAutoCString uuidString; + uuidString = Substring(fullUuidStr, 1, NSID_LENGTH - 3); + + ASSERT_STREQ( + uuidString.get(), + mozilla::glean::characteristics::client_identifier.TestGetValue() + .unwrap() + .value() + .get()); + ASSERT_EQ( + testing::MaxTouchPoints(), + mozilla::glean::characteristics::max_touch_points.TestGetValue() + .unwrap() + .ref()); + }); + nsUserCharacteristics::SubmitPing(); + ASSERT_TRUE(submitted); +} + +TEST(ResistFingerprinting, UserCharacteristics_ClearPref) +{ + nsCString originalUUID; + + mozilla::glean_pings::UserCharacteristics.TestBeforeNextSubmit( + [&originalUUID](const nsACString& aReason) { + originalUUID = + mozilla::glean::characteristics::client_identifier.TestGetValue() + .unwrap() + .value() + .get(); + ASSERT_STRNE("", mozilla::glean::characteristics::client_identifier + .TestGetValue() + .unwrap() + .value() + .get()); + + nsCString fullUuidStr; + Preferences::GetCString(kUUIDPref, fullUuidStr); + + // Remove the '{' and '}' + nsAutoCString uuidString; + uuidString = Substring(fullUuidStr, 1, NSID_LENGTH - 3); + + ASSERT_STREQ( + uuidString.get(), + mozilla::glean::characteristics::client_identifier.TestGetValue() + .unwrap() + .value() + .get()); + }); + nsUserCharacteristics::PopulateData(true); + nsUserCharacteristics::SubmitPing(); + + auto original_value = + Preferences::GetBool("datareporting.healthreport.uploadEnabled"); + Preferences::SetBool("datareporting.healthreport.uploadEnabled", true); + Preferences::SetBool("datareporting.healthreport.uploadEnabled", false); + + mozilla::glean_pings::UserCharacteristics.TestBeforeNextSubmit( + [](const nsACString& aReason) { + // Assert that the pref is blank + nsAutoCString uuidValue; + Preferences::GetCString(kUUIDPref, uuidValue); + ASSERT_STREQ("", uuidValue.get()); + }); + nsUserCharacteristics::SubmitPing(); + + Preferences::SetBool("datareporting.healthreport.uploadEnabled", true); + mozilla::glean_pings::UserCharacteristics.TestBeforeNextSubmit( + [&originalUUID](const nsACString& aReason) { + // Assert that the new UUID is different from the old one + ASSERT_STRNE( + originalUUID.get(), + mozilla::glean::characteristics::client_identifier.TestGetValue() + .unwrap() + .value() + .get()); + + // Assert that the pref is not blank + nsAutoCString uuidValue; + Preferences::GetCString(kUUIDPref, uuidValue); + ASSERT_STRNE("", uuidValue.get()); + }); + nsUserCharacteristics::PopulateData(true); + nsUserCharacteristics::SubmitPing(); + + Preferences::SetBool("datareporting.healthreport.uploadEnabled", + original_value); +} |