// 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::env; use std::ffi::{c_char, CStr, CString}; use std::ops::DerefMut; use std::path::PathBuf; use std::time::Duration;
use firefox_on_glean::{metrics, pings}; use nserror::{nsresult, NS_ERROR_FAILURE}; use nsstring::{nsACString, nsCString, nsString}; use xpcom::interfaces::{
mozILocaleService, nsIFile, nsIPrefService, nsIProperties, nsIXULAppInfo, nsIXULRuntime,
}; use xpcom::{RefPtr, XpCom};
use glean::{ClientInfoMetrics, Configuration};
#[cfg(not(target_os = "android"))] mod upload_pref; #[cfg(not(target_os = "android"))] mod user_activity; mod viaduct_uploader;
#[cfg(not(target_os = "android"))] use upload_pref::UploadPrefObserver; #[cfg(not(target_os = "android"))] use user_activity::UserActivityObserver; use viaduct_uploader::ViaductUploader;
/// Project FOG's entry point. /// /// This assembles client information and the Glean configuration and then initializes the global /// Glean instance. #[cfg(not(target_os = "android"))] #[no_mangle] pubextern"C"fn fog_init(
data_path_override: &nsACString,
app_id_override: &nsACString,
disable_internal_pings: bool,
) -> nsresult { let upload_enabled = static_prefs::pref!("datareporting.healthreport.uploadEnabled"); let recording_enabled = static_prefs::pref!("telemetry.fog.test.localhost_port") < 0; let uploader = Some(Box::new(ViaductUploader) asBox<dyn glean::net::PingUploader>);
fog_init_internal(
data_path_override,
app_id_override,
upload_enabled || recording_enabled,
uploader, // Flipping it around, because no value = defaults to false, // so we take in `disable` but pass on `enable`.
!disable_internal_pings,
)
.into()
}
/// Project FOG's entry point on Android. /// /// This assembles client information and the Glean configuration and then initializes the global /// Glean instance. /// It always enables upload and set no uploader. /// This should only be called in test scenarios. /// In normal use Glean should be initialized and controlled by the Glean Kotlin SDK. #[cfg(target_os = "android")] #[no_mangle] pubextern"C"fn fog_init(
data_path_override: &nsACString,
app_id_override: &nsACString,
disable_internal_pings: bool,
) -> nsresult { // On Android always enable Glean upload. let upload_enabled = true; // Don't set up an uploader. let uploader = None;
// If we're operating in automation without any specific source tags to set, // set the tag "automation" so any pings that escape don't clutter the tables. // See https://mozilla.github.io/glean/book/user/debugging/index.html#enabling-debugging-features-through-environment-variables if env::var("MOZ_AUTOMATION").is_ok() && env::var("GLEAN_SOURCE_TAGS").is_err() {
log::info!("In automation, setting 'automation' source tag.");
glean::set_source_tags(vec!["automation".to_string()]);
log::info!("In automation, disabling MPS to avoid 4am issues.");
conf.use_core_mps = false;
}
log::debug!("Configuration: {:#?}", conf);
// Register all custom pings before we initialize.
pings::register_pings(Some(&conf.application_id));
glean::initialize(conf, client_info);
metrics::fog::initialization.stop();
Ok(())
}
fn build_configuration(
data_path_override: &nsACString,
app_id_override: &nsACString,
) -> Result<(Configuration, ClientInfoMetrics), nsresult> { let data_path_str = if data_path_override.is_empty() {
get_data_path()?
} else {
data_path_override.to_utf8().to_string()
}; let data_path = PathBuf::from(&data_path_str);
let (app_build, app_display_version, channel, locale) = get_app_info()?;
let application_id = if app_id_override.is_empty() { "firefox.desktop".to_string()
} else {
app_id_override.to_utf8().to_string()
};
extern"C" { fn FOG_MaxPingLimit() -> u32;
}
// SAFETY NOTE: Safe because it returns a primitive by value. let pings_per_interval = unsafe { FOG_MaxPingLimit() };
metrics::fog::max_pings_per_minute.set(pings_per_interval.into());
let rate_limit = Some(glean::PingRateLimit {
seconds_per_interval: 60,
pings_per_interval,
});
#[cfg(not(target_os = "android"))] fn setup_observers() -> Result<(), nsresult> { iflet Err(e) = UploadPrefObserver::begin_observing() {
log::error!( "Could not observe data upload pref. Abandoning FOG init due to {:?}",
e
); return Err(e);
}
iflet Err(e) = UserActivityObserver::begin_observing() {
log::error!( "Could not observe user activity. Abandoning FOG init due to {:?}",
e
); return Err(e);
}
Ok(())
}
#[cfg(target_os = "android")] fn setup_observers() -> Result<(), nsresult> { // No observers are set up on Android.
Ok(())
}
/// Construct and return the data_path from the profile dir, or return an error. fn get_data_path() -> Result<String, nsresult> { let dir_svc: RefPtr<nsIProperties> = match xpcom::components::Directory::service() {
Ok(ds) => ds,
_ => return Err(NS_ERROR_FAILURE),
}; letmut profile_dir = xpcom::GetterAddrefs::<nsIFile>::new(); unsafe {
dir_svc
.Get(
cstr!("ProfD").as_ptr(),
&nsIFile::IID,
profile_dir.void_ptr(),
)
.to_result()?;
} let profile_dir = profile_dir.refptr().ok_or(NS_ERROR_FAILURE)?; letmut profile_path = nsString::new(); unsafe {
(*profile_dir).GetPath(&mut *profile_path).to_result()?;
} let profile_path = String::from_utf16(&profile_path[..]).map_err(|_| NS_ERROR_FAILURE)?; let data_path = profile_path + "/datareporting/glean";
Ok(data_path)
}
/// Return a tuple of the build_id, app version, build channel, and locale. /// If the XUL Runtime isn't a XULAppInfo (e.g. in xpcshell), /// build_id will be "unknown". /// Other problems result in an error being returned instead. fn get_app_info() -> Result<(String, String, String, String), nsresult> { let xul: RefPtr<nsIXULRuntime> =
xpcom::components::XULRuntime::service().map_err(|_| NS_ERROR_FAILURE)?;
let pref_service: RefPtr<nsIPrefService> =
xpcom::components::Preferences::service().map_err(|_| NS_ERROR_FAILURE)?; let locale_service: RefPtr<mozILocaleService> =
xpcom::components::Locale::service().map_err(|_| NS_ERROR_FAILURE)?; let branch = xpcom::getter_addrefs(|p| { // Safe because: // * `null` is explicitly allowed per documentation // * `p` is a valid outparam guaranteed by `getter_addrefs` unsafe { pref_service.GetDefaultBranch(std::ptr::null(), p) }
})?; let pref_name = CString::new("app.update.channel").map_err(|_| NS_ERROR_FAILURE)?; letmut channel = nsCString::new(); // Safe because: // * `branch` is non-null (otherwise `getter_addrefs` would've been `Err` // * `pref_name` exists so a pointer to it is valid for the life of the function // * `channel` exists so a pointer to it is valid, and it can be written to unsafe { if (*branch)
.GetCharPref(pref_name.as_ptr(), channel.deref_mut() as *mut nsACString)
.to_result()
.is_err()
{
channel = "unknown".into();
}
}
extern"C" { fn FOG_MozAppVersionDisplay() -> *const c_char;
} // SAFETY: It's literally a quoted literal. let version = unsafe { CStr::from_ptr(FOG_MozAppVersionDisplay()) }
.to_str()
.map_err(|_| NS_ERROR_FAILURE)?;
let app_info = match xul.query_interface::<nsIXULAppInfo>() {
Some(ai) => ai, // In e.g. xpcshell the XULRuntime isn't XULAppInfo. // We still want to return sensible values so tests don't explode.
_ => { return Ok(( "unknown".to_owned(),
version.to_string(),
channel.to_string(), "unknown".to_owned(),
))
}
};
/// **TEST-ONLY METHOD** /// Resets FOG and the underlying Glean SDK, clearing stores. #[no_mangle] pubextern"C"fn fog_test_reset(
data_path_override: &nsACString,
app_id_override: &nsACString,
) -> nsresult {
fog_test_reset_internal(data_path_override, app_id_override).into()
}
// Split out into its own function so I could use `?` #[cfg(not(target_os = "android"))] fn fog_test_reset_internal(
data_path_override: &nsACString,
app_id_override: &nsACString,
) -> Result<(), nsresult> { let (mut conf, client_info) = build_configuration(data_path_override, app_id_override)?;
let upload_enabled = static_prefs::pref!("datareporting.healthreport.uploadEnabled"); let recording_enabled = static_prefs::pref!("telemetry.fog.test.localhost_port") < 0;
conf.upload_enabled = upload_enabled || recording_enabled;
// I'd prefer to reuse the uploader, but it gets moved into Glean so we build anew.
conf.uploader = Some(Box::new(ViaductUploader) asBox<dyn glean::net::PingUploader>);
// Register all custom pings before we initialize.
pings::register_pings(None);
// Same as before, would prefer to reuse, but it gets moved into Glean so we build anew.
conf.uploader = Some(Box::new(ViaductUploader) asBox<dyn glean::net::PingUploader>);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.