Source code
Revision control
Copy as Markdown
Other Tools
/* 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
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
MozAdsClientBuilder:
"moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustAdsClient.sys.mjs",
MozAdsEnvironment:
"moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustAdsClient.sys.mjs",
MozAdsRequestOptions:
"moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustAdsClient.sys.mjs",
MozAdsTelemetry:
"moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustAdsClient.sys.mjs",
OhttpConfig:
"moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustViaduct.sys.mjs",
configureOhttpChannel:
"moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustViaduct.sys.mjs",
});
const PREF_ADSCLIENT_ENABLED = "unifiedAds.adsClient.enabled";
// Viaduct OHTTP channel the ads-client sends over (matches OHTTP_CHANNEL_ID in
// the vendored ads-client crate).
const OHTTP_CHANNEL_ID = "ads-client";
const PREF_OHTTP_ENABLED =
"browser.newtabpage.activity-stream.unifiedAds.ohttp.enabled";
const PREF_OHTTP_RELAY_URL =
"browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL";
const PREF_OHTTP_CONFIG_URL =
"browser.newtabpage.activity-stream.discoverystream.ohttp.configURL";
/**
* Manages the process-wide MozAdsClient singleton, exported below as
* `AdsClient`.
*/
export class _AdsClient {
#client;
/**
* @param {object} prefValues The New Tab store's Prefs.values.
* @returns {boolean}
*/
isEnabled(prefValues) {
return Boolean(
prefValues?.trainhopConfig?.adsClient?.enabled ||
prefValues?.[PREF_ADSCLIENT_ENABLED]
);
}
/**
* Build (once) and return the MozAdsClient singleton.
*
* @returns {?MozAdsClient} null if the bindings are unavailable.
*/
getClient() {
if (this.#client === undefined) {
this.#client = this.#build();
}
return this.#client;
}
/**
* Options for requestTileAds/requestSpocAds/record*, with the OHTTP channel
* configured from prefs.
*
* @returns {MozAdsRequestOptions}
*/
requestOptions() {
return new lazy.MozAdsRequestOptions({
flags: new Map(),
ohttp: this.#configureOhttp(),
});
}
/**
* Configure the viaduct OHTTP channel the ads-client sends over.
*
* @returns {boolean} Whether OHTTP is configured and should be requested.
*/
#configureOhttp() {
const enabled = Services.prefs.getBoolPref(PREF_OHTTP_ENABLED, false);
const relayUrl = Services.prefs.getStringPref(PREF_OHTTP_RELAY_URL, "");
const configUrl = Services.prefs.getStringPref(PREF_OHTTP_CONFIG_URL, "");
if (!enabled || !relayUrl || !configUrl) {
return false;
}
try {
lazy.configureOhttpChannel(
OHTTP_CHANNEL_ID,
new lazy.OhttpConfig({
relayUrl,
gatewayHost: new URL(configUrl).host,
})
);
return true;
} catch (error) {
console.error("MozAdsClient failed to configure OHTTP channel", error);
return false;
}
}
#build() {
// @backward-compat { version 154 }
// The ads-client bindings only exist on Fx154+, and the New Tab add-on can
// train-hop onto older Beta/Release builds. Bail out before touching the
// lazily-loaded lazy.MozAds* bindings. Remove once 154 reaches Release.
if (Services.vc.compare(AppConstants.MOZ_APP_VERSION, "154.0a1") < 0) {
return null;
}
try {
// Placeholder telemetry until Glean is wired up.
class LoggerTelemetry extends lazy.MozAdsTelemetry {
recordBuildCacheError(label, value) {
console.error(
"MozAdsClient telemetry: build cache error",
label,
value
);
}
recordClientError(label, value) {
console.error("MozAdsClient telemetry: client error", label, value);
}
recordClientOperationTotal(label) {
console.warn("MozAdsClient telemetry: client operation", label);
}
recordDeserializationError(label, value) {
console.error(
"MozAdsClient telemetry: deserialization error",
label,
value
);
}
recordHttpCacheOutcome(label, value) {
console.warn(
"MozAdsClient telemetry: http cache outcome",
label,
value
);
}
}
return lazy.MozAdsClientBuilder.init()
.environment(lazy.MozAdsEnvironment.PROD)
.telemetry(new LoggerTelemetry())
.build();
} catch (error) {
console.error("MozAdsClient failed to initialize", error);
return null;
}
}
}
export const AdsClient = new _AdsClient();