summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/xpcom
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/components/glean/xpcom/FOG.cpp363
-rw-r--r--toolkit/components/glean/xpcom/FOG.h29
-rw-r--r--toolkit/components/glean/xpcom/components.conf18
-rw-r--r--toolkit/components/glean/xpcom/moz.build27
-rw-r--r--toolkit/components/glean/xpcom/nsIFOG.idl178
-rw-r--r--toolkit/components/glean/xpcom/nsIGleanMetrics.idl665
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);
+};