summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/src/init/upload_pref.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/glean/src/init/upload_pref.rs')
-rw-r--r--toolkit/components/glean/src/init/upload_pref.rs99
1 files changed, 99 insertions, 0 deletions
diff --git a/toolkit/components/glean/src/init/upload_pref.rs b/toolkit/components/glean/src/init/upload_pref.rs
new file mode 100644
index 0000000000..737230c16c
--- /dev/null
+++ b/toolkit/components/glean/src/init/upload_pref.rs
@@ -0,0 +1,99 @@
+// 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/.
+
+use std::ffi::CStr;
+use std::os::raw::c_char;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+use nserror::{nsresult, NS_ERROR_FAILURE, NS_OK};
+use nsstring::{nsACString, nsCStr};
+use xpcom::{
+ interfaces::{nsIPrefBranch, nsISupports},
+ RefPtr,
+};
+
+/// Whether the current value of the localhost testing pref is permitting
+/// metric recording (even if upload is disabled).
+static RECORDING_ENABLED: AtomicBool = AtomicBool::new(false);
+
+// Partially cargo-culted from https://searchfox.org/mozilla-central/rev/598e50d2c3cd81cd616654f16af811adceb08f9f/security/manager/ssl/cert_storage/src/lib.rs#1192
+#[xpcom(implement(nsIObserver), atomic)]
+pub(crate) struct UploadPrefObserver {}
+
+#[allow(non_snake_case)]
+impl UploadPrefObserver {
+ pub(crate) fn begin_observing() -> Result<(), nsresult> {
+ // Ensure we begin with the correct current value of RECORDING_ENABLED.
+ let recording_enabled = static_prefs::pref!("telemetry.fog.test.localhost_port") < 0;
+ RECORDING_ENABLED.store(recording_enabled, Ordering::SeqCst);
+
+ // SAFETY: Everything here is self-contained.
+ //
+ // * We allocate the pref observer, created by the xpcom macro
+ // * We query the pref service and bail out if it doesn't exist.
+ // * We create a nsCStr from a static string.
+ // * We control all input to `AddObserverImpl`
+ unsafe {
+ let pref_obs = Self::allocate(InitUploadPrefObserver {});
+ let pref_branch: RefPtr<nsIPrefBranch> =
+ xpcom::components::Preferences::service().map_err(|_| NS_ERROR_FAILURE)?;
+ let pref_nscstr =
+ &nsCStr::from("datareporting.healthreport.uploadEnabled") as &nsACString;
+ (*pref_branch)
+ .AddObserverImpl(pref_nscstr, pref_obs.coerce(), false)
+ .to_result()?;
+ let pref_nscstr = &nsCStr::from("telemetry.fog.test.localhost_port") as &nsACString;
+ (*pref_branch)
+ .AddObserverImpl(pref_nscstr, pref_obs.coerce(), false)
+ .to_result()?;
+ }
+
+ Ok(())
+ }
+
+ unsafe fn Observe(
+ &self,
+ _subject: *const nsISupports,
+ topic: *const c_char,
+ pref_name: *const u16,
+ ) -> nserror::nsresult {
+ let topic = CStr::from_ptr(topic).to_str().unwrap();
+ // Conversion utf16 to utf8 is messy.
+ // We should only ever observe changes to one of the two prefs we want,
+ // but just to be on the safe side let's assert.
+
+ // cargo-culted from https://searchfox.org/mozilla-central/rev/598e50d2c3cd81cd616654f16af811adceb08f9f/security/manager/ssl/cert_storage/src/lib.rs#1606-1612
+ // (with a little transformation)
+ let len = (0..).take_while(|&i| *pref_name.offset(i) != 0).count(); // find NUL.
+ let slice = std::slice::from_raw_parts(pref_name, len);
+ let pref_name = match String::from_utf16(slice) {
+ Ok(name) => name,
+ Err(_) => return NS_ERROR_FAILURE,
+ };
+ log::info!("Observed {:?}, {:?}", topic, pref_name);
+ debug_assert!(topic == "nsPref:changed");
+ debug_assert!(
+ pref_name == "datareporting.healthreport.uploadEnabled"
+ || pref_name == "telemetry.fog.test.localhost_port"
+ );
+
+ let upload_enabled = static_prefs::pref!("datareporting.healthreport.uploadEnabled");
+ let recording_enabled = static_prefs::pref!("telemetry.fog.test.localhost_port") < 0;
+ log::info!(
+ "New upload_enabled {}, recording_enabled {}",
+ upload_enabled,
+ recording_enabled
+ );
+ if RECORDING_ENABLED.load(Ordering::SeqCst) && !recording_enabled {
+ // Whenever the test pref goes from permitting recording to forbidding it,
+ // ensure Glean is told to wipe the stores.
+ // This may send a "deletion-request" ping for a client_id that's never sent
+ // any other pings.
+ glean::set_upload_enabled(false);
+ }
+ RECORDING_ENABLED.store(recording_enabled, Ordering::SeqCst);
+ glean::set_upload_enabled(upload_enabled || recording_enabled);
+ NS_OK
+ }
+}