diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/glean/xpcom/FOG.cpp | 363 | ||||
-rw-r--r-- | toolkit/components/glean/xpcom/FOG.h | 29 | ||||
-rw-r--r-- | toolkit/components/glean/xpcom/components.conf | 18 | ||||
-rw-r--r-- | toolkit/components/glean/xpcom/moz.build | 27 | ||||
-rw-r--r-- | toolkit/components/glean/xpcom/nsIFOG.idl | 178 | ||||
-rw-r--r-- | toolkit/components/glean/xpcom/nsIGleanMetrics.idl | 665 |
6 files changed, 1280 insertions, 0 deletions
diff --git a/toolkit/components/glean/xpcom/FOG.cpp b/toolkit/components/glean/xpcom/FOG.cpp new file mode 100644 index 0000000000..a113ae86d9 --- /dev/null +++ b/toolkit/components/glean/xpcom/FOG.cpp @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "mozilla/FOG.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/FOGIPC.h" +#include "mozilla/glean/bindings/Common.h" +#include "mozilla/glean/bindings/jog/jog_ffi_generated.h" +#include "mozilla/glean/fog_ffi_generated.h" +#include "mozilla/glean/GleanMetrics.h" +#include "mozilla/MozPromise.h" +#include "mozilla/ShutdownPhase.h" +#include "mozilla/Unused.h" +#include "nsContentUtils.h" +#include "nsIFOG.h" +#include "nsIUserIdleService.h" +#include "nsServiceManagerUtils.h" + +namespace mozilla { + +using glean::LogToBrowserConsole; + +#ifdef MOZ_GLEAN_ANDROID +// Defined by `glean-core`. We reexport it here for later use. +extern "C" NS_EXPORT void glean_enable_logging(void); + +// Workaround to force a re-export of the `no_mangle` symbols from `glean-core` +// +// Due to how linking works and hides symbols the symbols from `glean-core` +// might not be re-exported and thus not usable. By forcing use of _at least +// one_ symbol in an exported function the functions will also be rexported. +// +// See also https://github.com/rust-lang/rust/issues/50007 +extern "C" NS_EXPORT void _fog_force_reexport_donotcall(void) { + glean_enable_logging(); +} +#endif + +static StaticRefPtr<FOG> gFOG; + +// We wait for 5s of idle before dumping IPC and flushing ping data to disk. +// This number hasn't been tuned, so if you have a reason to change it, +// please by all means do. +const uint32_t kIdleSecs = 5; + +// static +already_AddRefed<FOG> FOG::GetSingleton() { + if (gFOG) { + return do_AddRef(gFOG); + } + + gFOG = new FOG(); + + if (XRE_IsParentProcess()) { + nsresult rv; + nsCOMPtr<nsIUserIdleService> idleService = + do_GetService("@mozilla.org/widget/useridleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, nullptr); + MOZ_ASSERT(idleService); + if (NS_WARN_IF(NS_FAILED(idleService->AddIdleObserver(gFOG, kIdleSecs)))) { + glean::fog::failed_idle_registration.Set(true); + } + + RunOnShutdown( + [&] { + nsresult rv; + nsCOMPtr<nsIUserIdleService> idleService = + do_GetService("@mozilla.org/widget/useridleservice;1", &rv); + if (NS_SUCCEEDED(rv)) { + MOZ_ASSERT(idleService); + Unused << idleService->RemoveIdleObserver(gFOG, kIdleSecs); + } + gFOG->Shutdown(); + gFOG = nullptr; + }, + ShutdownPhase::XPCOMShutdown); + } + return do_AddRef(gFOG); +} + +void FOG::Shutdown() { + MOZ_ASSERT(XRE_IsParentProcess()); + glean::impl::fog_shutdown(); +} + +NS_IMETHODIMP +FOG::InitializeFOG(const nsACString& aDataPathOverride, + const nsACString& aAppIdOverride) { + MOZ_ASSERT(XRE_IsParentProcess()); + return glean::impl::fog_init(&aDataPathOverride, &aAppIdOverride); +} + +NS_IMETHODIMP +FOG::RegisterCustomPings() { + MOZ_ASSERT(XRE_IsParentProcess()); + glean::impl::fog_register_pings(); + return NS_OK; +} + +NS_IMETHODIMP +FOG::SetLogPings(bool aEnableLogPings) { +#ifdef MOZ_GLEAN_ANDROID + return NS_OK; +#else + MOZ_ASSERT(XRE_IsParentProcess()); + return glean::impl::fog_set_log_pings(aEnableLogPings); +#endif +} + +NS_IMETHODIMP +FOG::SetTagPings(const nsACString& aDebugTag) { +#ifdef MOZ_GLEAN_ANDROID + return NS_OK; +#else + MOZ_ASSERT(XRE_IsParentProcess()); + return glean::impl::fog_set_debug_view_tag(&aDebugTag); +#endif +} + +NS_IMETHODIMP +FOG::SendPing(const nsACString& aPingName) { +#ifdef MOZ_GLEAN_ANDROID + return NS_OK; +#else + MOZ_ASSERT(XRE_IsParentProcess()); + return glean::impl::fog_submit_ping(&aPingName); +#endif +} + +NS_IMETHODIMP +FOG::SetExperimentActive(const nsACString& aExperimentId, + const nsACString& aBranch, JS::HandleValue aExtra, + JSContext* aCx) { +#ifdef MOZ_GLEAN_ANDROID + NS_WARNING("Don't set experiments from Gecko in Android. Ignoring."); + return NS_OK; +#else + MOZ_ASSERT(XRE_IsParentProcess()); + nsTArray<nsCString> extraKeys; + nsTArray<nsCString> extraValues; + if (!aExtra.isNullOrUndefined()) { + JS::RootedObject obj(aCx, &aExtra.toObject()); + JS::Rooted<JS::IdVector> keys(aCx, JS::IdVector(aCx)); + if (!JS_Enumerate(aCx, obj, &keys)) { + LogToBrowserConsole(nsIScriptError::warningFlag, + u"Failed to enumerate experiment extras object."_ns); + return NS_OK; + } + + for (size_t i = 0, n = keys.length(); i < n; i++) { + nsAutoJSCString jsKey; + if (!jsKey.init(aCx, keys[i])) { + LogToBrowserConsole( + nsIScriptError::warningFlag, + u"Extra dictionary should only contain string keys."_ns); + return NS_OK; + } + + JS::Rooted<JS::Value> value(aCx); + if (!JS_GetPropertyById(aCx, obj, keys[i], &value)) { + LogToBrowserConsole(nsIScriptError::warningFlag, + u"Failed to get experiment extra property."_ns); + return NS_OK; + } + + nsAutoJSCString jsValue; + if (!value.isString()) { + LogToBrowserConsole( + nsIScriptError::warningFlag, + u"Experiment extra properties must have string values."_ns); + return NS_OK; + } + + if (!jsValue.init(aCx, value)) { + LogToBrowserConsole(nsIScriptError::warningFlag, + u"Can't extract experiment extra property"_ns); + return NS_OK; + } + + extraKeys.AppendElement(jsKey); + extraValues.AppendElement(jsValue); + } + } + glean::impl::fog_set_experiment_active(&aExperimentId, &aBranch, &extraKeys, + &extraValues); + return NS_OK; +#endif +} + +NS_IMETHODIMP +FOG::SetExperimentInactive(const nsACString& aExperimentId) { +#ifdef MOZ_GLEAN_ANDROID + NS_WARNING("Don't unset experiments from Gecko in Android. Ignoring."); + return NS_OK; +#else + MOZ_ASSERT(XRE_IsParentProcess()); + glean::impl::fog_set_experiment_inactive(&aExperimentId); + return NS_OK; +#endif +} + +NS_IMETHODIMP +FOG::TestGetExperimentData(const nsACString& aExperimentId, JSContext* aCx, + JS::MutableHandleValue aResult) { +#ifdef MOZ_GLEAN_ANDROID + NS_WARNING("Don't test experiments from Gecko in Android. Throwing."); + aResult.set(JS::UndefinedValue()); + return NS_ERROR_FAILURE; +#else + MOZ_ASSERT(XRE_IsParentProcess()); + if (!glean::impl::fog_test_is_experiment_active(&aExperimentId)) { + aResult.set(JS::UndefinedValue()); + return NS_OK; + } + + // We could struct-up the branch and extras and do what + // EventMetric::TestGetValue does... but keeping allocation on this side feels + // cleaner to me at the moment. + nsCString branch; + nsTArray<nsCString> extraKeys; + nsTArray<nsCString> extraValues; + + glean::impl::fog_test_get_experiment_data(&aExperimentId, &branch, &extraKeys, + &extraValues); + MOZ_ASSERT(extraKeys.Length() == extraValues.Length()); + + JS::RootedObject jsExperimentDataObj(aCx, JS_NewPlainObject(aCx)); + if (NS_WARN_IF(!jsExperimentDataObj)) { + return NS_ERROR_FAILURE; + } + + JS::RootedValue jsBranchStr(aCx); + if (!dom::ToJSValue(aCx, branch, &jsBranchStr) || + !JS_DefineProperty(aCx, jsExperimentDataObj, "branch", jsBranchStr, + JSPROP_ENUMERATE)) { + NS_WARNING("Failed to define branch for experiment data object."); + return NS_ERROR_FAILURE; + } + + JS::RootedObject jsExtraObj(aCx, JS_NewPlainObject(aCx)); + if (!JS_DefineProperty(aCx, jsExperimentDataObj, "extra", jsExtraObj, + JSPROP_ENUMERATE)) { + NS_WARNING("Failed to define extra for experiment data object."); + return NS_ERROR_FAILURE; + } + + for (unsigned int i = 0; i < extraKeys.Length(); i++) { + JS::RootedValue jsValueStr(aCx); + if (!dom::ToJSValue(aCx, extraValues[i], &jsValueStr) || + !JS_DefineProperty(aCx, jsExtraObj, extraKeys[i].Data(), jsValueStr, + JSPROP_ENUMERATE)) { + NS_WARNING("Failed to define extra property for experiment data object."); + return NS_ERROR_FAILURE; + } + } + aResult.setObject(*jsExperimentDataObj); + return NS_OK; +#endif +} + +NS_IMETHODIMP +FOG::TestFlushAllChildren(JSContext* aCx, mozilla::dom::Promise** aOutPromise) { + MOZ_ASSERT(XRE_IsParentProcess()); + NS_ENSURE_ARG(aOutPromise); + *aOutPromise = nullptr; + nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); + if (NS_WARN_IF(!global)) { + return NS_ERROR_FAILURE; + } + + ErrorResult erv; + RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv); + if (NS_WARN_IF(erv.Failed())) { + return erv.StealNSResult(); + } + + glean::FlushAndUseFOGData()->Then( + GetCurrentSerialEventTarget(), __func__, + [promise]() { promise->MaybeResolveWithUndefined(); }); + + promise.forget(aOutPromise); + return NS_OK; +} + +NS_IMETHODIMP +FOG::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + // On idle, opportunistically flush child process data to the parent, + // then persist ping-lifetime data to the db. + if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE)) { + glean::FlushAndUseFOGData(); +#ifndef MOZ_GLEAN_ANDROID + Unused << glean::impl::fog_persist_ping_lifetime_data(); +#endif + } + + return NS_OK; +} + +NS_IMETHODIMP +FOG::TestResetFOG(const nsACString& aDataPathOverride, + const nsACString& aAppIdOverride) { + MOZ_ASSERT(XRE_IsParentProcess()); + return glean::impl::fog_test_reset(&aDataPathOverride, &aAppIdOverride); +} + +NS_IMETHODIMP +FOG::TestTriggerMetrics(uint32_t aProcessType, JSContext* aCx, + mozilla::dom::Promise** aOutPromise) { + MOZ_ASSERT(XRE_IsParentProcess()); + NS_ENSURE_ARG(aOutPromise); + *aOutPromise = nullptr; + nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); + if (NS_WARN_IF(!global)) { + return NS_ERROR_FAILURE; + } + + ErrorResult erv; + RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv); + if (NS_WARN_IF(erv.Failed())) { + return erv.StealNSResult(); + } + + glean::TestTriggerMetrics(aProcessType, promise); + + promise.forget(aOutPromise); + return NS_OK; +} + +NS_IMETHODIMP +FOG::TestRegisterRuntimeMetric( + const nsACString& aType, const nsACString& aCategory, + const nsACString& aName, const nsTArray<nsCString>& aPings, + const nsACString& aLifetime, const bool aDisabled, + const nsACString& aExtraArgs, uint32_t* aMetricIdOut) { + *aMetricIdOut = 0; + *aMetricIdOut = glean::jog::jog_test_register_metric( + &aType, &aCategory, &aName, &aPings, &aLifetime, aDisabled, &aExtraArgs); + return NS_OK; +} + +NS_IMETHODIMP +FOG::TestRegisterRuntimePing(const nsACString& aName, + const bool aIncludeClientId, + const bool aSendIfEmpty, + const nsTArray<nsCString>& aReasonCodes, + uint32_t* aPingIdOut) { + *aPingIdOut = 0; + *aPingIdOut = glean::jog::jog_test_register_ping(&aName, aIncludeClientId, + aSendIfEmpty, &aReasonCodes); + return NS_OK; +} + +NS_IMPL_ISUPPORTS(FOG, nsIFOG, nsIObserver) + +} // namespace mozilla diff --git a/toolkit/components/glean/xpcom/FOG.h b/toolkit/components/glean/xpcom/FOG.h new file mode 100644 index 0000000000..55e155052e --- /dev/null +++ b/toolkit/components/glean/xpcom/FOG.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 8; 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_FOG_h +#define mozilla_FOG_h + +#include "nsIFOG.h" +#include "nsIObserver.h" + +namespace mozilla { +class FOG final : public nsIFOG, public nsIObserver { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIFOG + NS_DECL_NSIOBSERVER + + public: + FOG() = default; + static already_AddRefed<FOG> GetSingleton(); + + private: + ~FOG() = default; + void Shutdown(); +}; + +}; // namespace mozilla + +#endif // mozilla_FOG_h diff --git a/toolkit/components/glean/xpcom/components.conf b/toolkit/components/glean/xpcom/components.conf new file mode 100644 index 0000000000..b5a86537c0 --- /dev/null +++ b/toolkit/components/glean/xpcom/components.conf @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Classes = [ + { + 'cid': '{98d0e975-9cad-4ce3-ae2f-f878b8be6307}', + 'contract_ids': ['@mozilla.org/toolkit/glean;1'], + 'singleton': True, + 'type': 'mozilla::FOG', + 'headers': ['mozilla/FOG.h'], + 'constructor': 'mozilla::FOG::GetSingleton', + 'js_name': 'fog', + 'interfaces': ['nsIFOG'], + } +] diff --git a/toolkit/components/glean/xpcom/moz.build b/toolkit/components/glean/xpcom/moz.build new file mode 100644 index 0000000000..3a9ededa44 --- /dev/null +++ b/toolkit/components/glean/xpcom/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +FINAL_LIBRARY = "xul" + +EXPORTS.mozilla += [ + "FOG.h", +] + +UNIFIED_SOURCES += [ + "FOG.cpp", +] + +XPCOM_MANIFESTS += ["components.conf"] + +XPIDL_MODULE = "fog" + +XPIDL_SOURCES += [ + "nsIFOG.idl", + "nsIGleanMetrics.idl", +] + +with Files("**"): + BUG_COMPONENT = ("Toolkit", "Telemetry") diff --git a/toolkit/components/glean/xpcom/nsIFOG.idl b/toolkit/components/glean/xpcom/nsIFOG.idl new file mode 100644 index 0000000000..3c3829db95 --- /dev/null +++ b/toolkit/components/glean/xpcom/nsIFOG.idl @@ -0,0 +1,178 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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 "nsISupports.idl" + +[scriptable, uuid(98d0e975-9cad-4ce3-ae2f-f878b8be6307)] +interface nsIFOG : nsISupports +{ + + /** + * Initialize FOG. + * + * To be scheduled at some opportune time after the bulk of Firefox startup + * has completed. + * + * @param aDataPathOverride - The path of a custom Glean data path to use + * instead of the profile dir. + * @param aAppIdOverride - The application_id to use instead of + * "firefox.desktop". + */ + void initializeFOG([optional] in AUTF8String aDataPathOverride, [optional] in AUTF8String aAppIdOverride); + + /** + * Register custom pings. + * + * Ensure all custom pings are registered with Glean. + */ + void registerCustomPings(); + + /** + * Enable or Disable the logging of pings in the Glean SDK. + * See https://firefox-source-docs.mozilla.org/toolkit/components/glean/testing.html + * for details. + * + * @param aEnableLogPings - true to enable logging, false to disable. + */ + void setLogPings(in boolean aEnableLogPings); + + /** + * Set the tag to be applied to pings assembled from now on. + * See https://firefox-source-docs.mozilla.org/toolkit/components/glean/testing.html + * for details. + * + * @param aDebugTag - The string tag to apply. + * If it cannot be applied (e.g it contains characters that are + * forbidden in HTTP headers) the old value will remain. + */ + void setTagPings(in ACString aDebugTag); + + /** + * Send the named ping. + * See https://firefox-source-docs.mozilla.org/toolkit/components/glean/testing.html + * for details. + * + * @param aPingName - The name of the ping to send. If no ping of that name + * exists, or the ping is known but cannot be assembled + * (e.g if it is empty), no ping will be sent. + */ + void sendPing(in ACString aPingName); + + /** + * Indicate that an experiment is running. + * Glean will add an experiment annotation which is sent with pings. + * This information is not persisted between runs. + * + * See `glean_core::Glean::set_experiment_active`. + * + * Logs on error, but does not throw. + * + * @param aExperimentId - The id/slug of the experiment. + * @param aBranch - The name of the active branch of the experiment. + * @param aExtra - Optional string -> string dictionary of extra information. + */ + [implicit_jscontext] + void setExperimentActive(in ACString aExperimentId, in ACString aBranch, [optional] in jsval aExtra); + + /** + * Indicate that an experiment is no longer running. + * + * See `glean_core::Glean::set_experiment_inactive`. + * + * Logs on error, but does not throw. + * + * @param aExperimentId - The id/slug of the experiment from setExperimentActive. + */ + void setExperimentInactive(in ACString aExperimentId); + + /** + * **Test-only API** + * + * If the identified experiment was set active and hasn't been set inactive, + * this will give you the active branch and extra information. + * + * @param aExperimentId - The id/slug of the experiment from setExperimentActive. + * + * @return an object of the form + * {branch: "branch-name", extra: {extra_key1: extra_value1, ...}} + * if there is an active experiment. Undefined, otherwise. + */ + [implicit_jscontext] + jsval testGetExperimentData(in ACString aExperimentId); + + /** + * ** Test-only Method ** + * + * Flush all data from all child processes. + * + * @returns A promise that resolves when the data's been stored. + */ + [implicit_jscontext] + Promise testFlushAllChildren(); + + /** + * ** Test-only Method ** + * + * Reset FOG and the Glean SDK, clearing storage. + */ + void testResetFOG([optional] in AUTF8String aDataPathOverride, [optional] in AUTF8String aAppIdOverride); + + /** + * ** Test-only Method ** + * + * Trigger test metric instrumentation on the GPU, RDD or Socket process. + * + * @param aProcessType - A PROCESS_TYPE_* value from the constants defined + * in the nsIXULRuntime interface. + * + * @returns A promise that resolves when the test data has been added. + * The promise will be rejected if the process type is not supported + * or if sending the IPC to the child process fails. + */ + [implicit_jscontext] + Promise testTriggerMetrics(in unsigned long aProcessType); + + /** + * ** Test-only Method ** + * + * Register a metric. + * + * This function is deliberately not too friendly to use. You probably aren't + * supposed to use it unless you're testing metric registration itself. + * + * @param aType - The metric's type. + * @param aCategory - The metric's category. + * @param aName - The metric's name. + * @param aPings - The pings to send it in. + * @param aLifetime - The metric's lifetime. + * @param aDisabled - Whether the metric, though existing, isn't enabled. + * @param aExtraArgs - Optional JSON string of extra args. + */ + uint32_t testRegisterRuntimeMetric(in ACString aType, + in ACString aCategory, + in ACString aName, + in Array<ACString> aPings, + in ACString aLifetime, + in boolean aDisabled, + [optional] in ACString aExtraArgs); + + /** + * ** Test-only Method ** + * + * Register a ping. + * + * This function is deliberately not too friendly to use. You probably aren't + * supposed to use it unless you're testing ping registration itself. + * + * @param aName - The ping's name. + * @param aIncludeClientId - Whether the ping should include the client_id. + * @param aSendIfEmpty - Whether the ping should send even if empty. + * @param aReasonCodes - The list of valid reasons for ping submission. + */ + uint32_t testRegisterRuntimePing(in ACString aName, + in boolean aIncludeClientId, + in boolean aSendIfEmpty, + in Array<ACString> aReasonCodes); +}; diff --git a/toolkit/components/glean/xpcom/nsIGleanMetrics.idl b/toolkit/components/glean/xpcom/nsIGleanMetrics.idl new file mode 100644 index 0000000000..b4faf26be5 --- /dev/null +++ b/toolkit/components/glean/xpcom/nsIGleanMetrics.idl @@ -0,0 +1,665 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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 "nsISupports.idl" + +[scriptable, uuid(d3180fe0-19fa-11eb-8b6f-0800200c9a66)] +interface nsIGleanBoolean : nsISupports +{ + /** + * Set to the specified boolean value. + * + * @param value the value to set. + */ + void set(in bool value); + + /** + * **Test-only API** + * + * Gets the currently stored value as a boolean. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(aa15fd20-1e8a-11eb-9bec-0800200c9a66)] +interface nsIGleanDatetime : nsISupports +{ + /** + * Set the datetime to the provided value, or the local now. + * The internal value will store the local timezone. + * + * Note: The metric's time_unit affects the resolution of the value, not the + * unit of this function's parameter (which is always PRTime/nanos). + * + * @param aValue The (optional) time value as PRTime (nanoseconds since epoch). + * Defaults to local now. + */ + [optional_argc] + void set([optional] in PRTime aValue); + + /** + * **Test-only API** + * + * Gets the currently stored value as an integer. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric as a JS Date with timezone, + * or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(05b89d2a-d57c-11ea-82da-3f63399a6f5a)] +interface nsIGleanCounter : nsISupports +{ + /* + * Increases the counter by `amount`. + * + * @param amount The amount to increase by. Should be positive. + */ + void add(in int32_t amount); + + /** + * **Test-only API** + * + * Gets the currently stored value as an integer. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(92e14730-9b5f-45a1-b018-f588d0b964d8)] +interface nsIGleanTimingDistribution : nsISupports +{ + /** + * Starts tracking time for the provided metric. + * + * @returns A unique timer id for the new timer + */ + [implicit_jscontext] + jsval start(); + + /** + * Stops tracking time for the provided metric and timer id. + * + * Adds a count to the corresponding bucket in the timing distribution. + * This will record an error if no `start` was called for this TimerId or + * if this TimerId was used to call `cancel`. + * + * @param aId The TimerId associated with this timing. This allows for + * concurrent timing of events associated with different ids. + */ + void stopAndAccumulate(in uint64_t aId); + + /** + * Aborts a previous `start` call. No error is recorded if no `start` was + * called. (But then where did you get that id from?) + * + * @param aId The TimerID whose `start` you wish to abort. + */ + void cancel(in uint64_t aId); + + /** + * **Test-only API** + * + * Gets the currently stored value as a DistributionData. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in ACString aPingName); + + /** + * **Test-only API** + * + * Accumulates a raw numeric sample of milliseconds. + * + * Test-only until we find a use-case and decent JS Time Duration type. + * + * @param aSample The sample, in milliseconds, to add. + */ + void testAccumulateRawMillis(in uint64_t aSample); +}; + +[scriptable, uuid(eea5ed46-16ba-46cd-bb1f-504581987fe1)] +interface nsIGleanMemoryDistribution : nsISupports +{ + /* + * Accumulates the provided sample in the metric. + * + * @param aSample The sample to be recorded by the metric. The sample is + * assumed to be in the confgured memory unit of the metric. + * + * Notes: Values bigger than 1 Terabyte (2^40 bytes) are truncated and an + * InvalidValue error is recorded. + */ + void accumulate(in uint64_t aSample); + + /** + * **Test-only API** + * + * Gets the currently stored value as a DistributionData. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in ACString aPingName); +}; + +[scriptable, uuid(45cc016f-c1d5-4d54-aaa5-a802cf65f23b)] +interface nsIGleanCustomDistribution : nsISupports +{ + /* + * Accumulates the provided signed samples in the metric. + * + * @param aSamples - The vector holding the samples to be recorded by the metric. + * + * Notes: Discards any negative value in `samples` + * and report an `ErrorType::InvalidValue` for each of them. + */ + void accumulateSamples(in Array<int64_t> aSamples); + + /** + * **Test-only API** + * + * Gets the currently stored value as a DistributionData. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in ACString aPingName); +}; + +[scriptable, function, uuid(e5447f62-4b03-497c-81e9-6ab683d20380)] +interface nsIGleanPingTestCallback : nsISupports +{ + void call(in ACString aReason); +}; + +[scriptable, uuid(5223a48b-687d-47ff-a629-fd4a72d1ecfa)] +interface nsIGleanPing : nsISupports +{ + /** + * Collect and submit the ping for eventual upload. + * + * This will collect all stored data to be included in the ping. + * Data with lifetime `ping` will then be reset. + * + * If the ping is configured with `send_if_empty = false` + * and the ping currently contains no content, + * it will not be queued for upload. + * If the ping is configured with `send_if_empty = true` + * it will be queued for upload even if empty. + * + * Pings always contain the `ping_info` and `client_info` sections. + * See [ping sections](https://mozilla.github.io/glean/book/user/pings/index.html#ping-sections) + * for details. + * + * @param aReason - Optional. The reason the ping is being submitted. + * Must match one of the configured `reason_codes`. + */ + void submit([optional] in ACString aReason); + + /** + * **Test-only API** + * + * Register a callback to be called right before this ping is next submitted. + * The provided function is called exactly once before submitting. + * + * Note: The callback will be called on any call to submit. + * A ping might not be sent afterwards, e.g. if the ping is empty and + * `send_if_empty` is `false`. + * + * @param aCallback - The callback to call on the next submit. + */ + void testBeforeNextSubmit(in nsIGleanPingTestCallback aCallback); +}; + +[scriptable, uuid(d84a3555-46f1-48c1-9122-e8e88b069d2b)] +interface nsIGleanString : nsISupports +{ + /* + * Set to the specified value. + * + * @param value The string to set the metric to. + */ + void set(in AUTF8String value); + + /** + * **Test-only API** + * + * Gets the currently stored value as a string. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(46751205-2ac7-47dc-91d2-ef4a95ef2af9)] +interface nsIGleanStringList : nsISupports +{ + /** + * Adds a new string to the list. + * + * Truncates the value and logs an error if it is longer than 50 bytes. + * + * @param value The string to add. + */ + void add(in AUTF8String value); + + /** + * Sets to a specific list of strings. + * + * Truncates the list and logs an error if longer than 20 items. + * Truncates any item longer than 50 bytes and logs an error. + * + * @param value The list of strings to set. + */ + void set(in Array<AUTF8String> value); + + /** + * **Test-only API** + * + * Gets the currently stored value. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(2586530c-030f-11eb-93cb-cbf30d25225a)] +interface nsIGleanTimespan : nsISupports +{ + /** + * Start tracking time for the provided metric. + * + * This records an error if it’s already tracking time (i.e. start was already + * called with no corresponding [stop]): in that case the original + * start time will be preserved. + */ + void start(); + + /** + * Stop tracking time for the provided metric. + * + * Sets the metric to the elapsed time, but does not overwrite an already + * existing value. + * This will record an error if no [start] was called or there is an already + * existing value. + */ + void stop(); + + /** + * Aborts a previous start. + * + * Does not record an error if there was no previous call to start. + */ + void cancel(); + + /** + * Explicitly sets the timespan value. + * + * This API should only be used if you cannot make use of + * `start`/`stop`/`cancel`. + * + * @param aDuration The duration of this timespan, in units matching the + * `time_unit` of this metric's definition. + */ + void setRaw(in uint32_t aDuration); + + /** + * **Test-only API** + * + * Gets the currently stored value as an integer. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(395700e7-06f6-46be-adcc-ea58977fda6d)] +interface nsIGleanUuid : nsISupports +{ + /** + * Set to the specified value. + * + * @param aValue The UUID to set the metric to. + */ + void set(in AUTF8String aValue); + + /** + * Generate a new random UUID and set the metric to it. + */ + void generateAndSet(); + + /** + * **Test-only API** + * + * Gets the currently stored value as an integer. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(1b01424a-1f55-11eb-92a5-0754f6c3f240)] +interface nsIGleanEvent : nsISupports +{ + /* + * Record an event. + * + * @param aExtra An (optional) map of extra values. + */ + [implicit_jscontext] + void record([optional] in jsval aExtra); + + /** + * **Test-only API** + * + * Get a list of currently stored events for this event metric. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + * + * The data is an array of objects: + * + * ``` + * [ + * { + * timestamp: Integer, + * category: String, + * name: String, + * extra: { + * String: String + * ... + * } + * }, + * ... + * ] + * ``` + * + * The difference between event timestamps is in milliseconds + * See https://mozilla.github.io/glean/book/user/metrics/event.html for further details. + * Due to limitations of numbers in JavaScript, the timestamp will only be accurate up until 2^53. + * (This is probably not an issue with the current clock implementation. Probably.) + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(0558c1b2-2cb1-4e21-a0a0-6a91a35ef219)] +interface nsIGleanQuantity : nsISupports +{ + /** + * Set to the specified value. + * + * @param value the value to set. + */ + void set(in int64_t value); + + /** + * **Test-only API** + * + * Gets the currently stored value. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(394d9d3b-9e7e-48cc-b76c-a89a51830da3)] +interface nsIGleanDenominator : nsISupports +{ + /* + * Increases the counter by `amount`. + * + * @param amount The amount to increase by. Should be positive. + */ + void add(in int32_t amount); + + /** + * **Test-only API** + * + * Gets the currently stored value as an integer. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(153fff71-7edd-49b4-a166-4697aa89c7a1)] +interface nsIGleanNumerator : nsISupports +{ + /* + * Increases the numerator by `amount`. + * + * @param amount The amount to increase by. Should be positive. + */ + void addToNumerator(in int32_t amount); + + /** + * **Test-only API** + * + * Gets the currently stored value in the form {numerator: n, denominator: d} + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(920cf631-2b1e-4efe-ae2e-f03277c3112a)] +interface nsIGleanRate : nsISupports +{ + /* + * Increases the numerator by `amount`. + * + * @param amount The amount to increase by. Should be positive. + */ + void addToNumerator(in int32_t amount); + + /* + * Increases the denominator by `amount`. + * + * @param amount The amount to increase by. Should be positive. + */ + void addToDenominator(in int32_t amount); + + /** + * **Test-only API** + * + * Gets the currently stored value in the form {numerator: n, denominator: d} + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; + +[scriptable, uuid(a59672c4-bc48-4bfe-8f9c-6f408a59d819)] +interface nsIGleanUrl : nsISupports +{ + /* + * Sets to the specified stringified URL. + * + * @param value The stringified URL to set the metric to. + */ + void set(in AUTF8String value); + + /** + * **Test-only API** + * + * Gets the currently stored value as a string. + * + * This function will attempt to await the last parent-process task (if any) + * writing to the the metric's storage engine before returning a value. + * This function will not wait for data from child processes. + * + * This doesn't clear the stored value. + * Parent process only. Panics in child processes. + * + * @param aPingName The (optional) name of the ping to retrieve the metric + * for. Defaults to the first value in `send_in_pings`. + * + * @return value of the stored metric, or undefined if there is no value. + */ + [implicit_jscontext] + jsval testGetValue([optional] in AUTF8String aPingName); +}; |