Skip to content

Commit 05f593b

Browse files
committed
Bug 1870138 - Add remote settings support for search overrides. r=Standard8
Differential Revision: https://phabricator.services.mozilla.com/D196490
1 parent e093365 commit 05f593b

21 files changed

+650
-10
lines changed

browser/components/search/BrowserSearchTelemetry.sys.mjs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ ChromeUtils.defineESModuleGetters(lazy, {
1010
UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.sys.mjs",
1111
});
1212

13+
// `contextId` is a unique identifier used by Contextual Services
14+
const CONTEXT_ID_PREF = "browser.contextual-services.contextId";
15+
ChromeUtils.defineLazyGetter(lazy, "contextId", () => {
16+
let _contextId = Services.prefs.getStringPref(CONTEXT_ID_PREF, null);
17+
if (!_contextId) {
18+
_contextId = Services.uuid.generateUUID().toString();
19+
Services.prefs.setStringPref(CONTEXT_ID_PREF, _contextId);
20+
}
21+
return _contextId;
22+
});
23+
1324
// A map of known search origins.
1425
// The keys of this map are used in the calling code to recordSearch, and in
1526
// the SEARCH_COUNTS histogram.
@@ -171,6 +182,10 @@ class BrowserSearchTelemetryHandler {
171182
* @throws if source is not in the known sources list.
172183
*/
173184
recordSearch(browser, engine, source, details = {}) {
185+
if (engine.clickUrl) {
186+
this.#reportSearchInGlean(engine.clickUrl);
187+
}
188+
174189
try {
175190
if (!this.shouldRecordSearchCount(browser)) {
176191
return;
@@ -281,6 +296,33 @@ class BrowserSearchTelemetryHandler {
281296
}
282297
);
283298
}
299+
300+
/**
301+
* Records the search in Glean for contextual services.
302+
*
303+
* @param {string} reportingUrl
304+
* The url to be sent to contextual services.
305+
*/
306+
#reportSearchInGlean(reportingUrl) {
307+
let defaultValuesByGleanKey = {
308+
contextId: lazy.contextId,
309+
};
310+
311+
let sendGleanPing = valuesByGleanKey => {
312+
valuesByGleanKey = { ...defaultValuesByGleanKey, ...valuesByGleanKey };
313+
for (let [gleanKey, value] of Object.entries(valuesByGleanKey)) {
314+
let glean = Glean.searchWith[gleanKey];
315+
if (value !== undefined && value !== "") {
316+
glean.set(value);
317+
}
318+
}
319+
GleanPings.searchWith.submit();
320+
};
321+
322+
sendGleanPing({
323+
reportingUrl,
324+
});
325+
}
284326
}
285327

286328
export var BrowserSearchTelemetry = new BrowserSearchTelemetryHandler();

browser/components/search/metrics.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,38 @@ serp:
318318
notification_emails:
319319
- fx-search-telemetry@mozilla.com
320320
expires: never
321+
322+
search_with:
323+
reporting_url:
324+
type: url
325+
description: >
326+
The external url to report this interaction to.
327+
bugs:
328+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1870138
329+
data_reviews:
330+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1870138
331+
data_sensitivity:
332+
- web_activity
333+
notification_emails:
334+
- mkaply@mozilla.com
335+
expires: never
336+
send_in_pings:
337+
- search-with
338+
339+
context_id:
340+
type: uuid
341+
description: >
342+
An identifier for Contextual Services user interaction pings. This is
343+
used internally for counting unique users as well as for anti-fraud. It
344+
is shared with other Contextual Services. It is not shared externally.
345+
bugs:
346+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1870138
347+
data_reviews:
348+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1870138#c3
349+
data_sensitivity:
350+
- technical
351+
notification_emails:
352+
- mkaply@mozilla.com
353+
expires: never
354+
send_in_pings:
355+
- search-with

browser/components/search/pings.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
---
6+
$schema: moz://mozilla.org/schemas/glean/pings/2-0-0
7+
8+
search-with:
9+
description: |
10+
A ping representing a "This time, search with" event with a partner search.
11+
Does not contain a `client_id`, preferring a `context_id` instead.
12+
The `context_id` is used internally for counting unique sers as well as for
13+
anti-fraud. It is shared with other Contextual Services. It is not shared
14+
externally.
15+
16+
include_client_id: false
17+
bugs:
18+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1870138
19+
data_reviews:
20+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1870138
21+
notification_emails:
22+
- mkaply@mozilla.com

services/settings/dumps/main/moz.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ FINAL_TARGET_FILES.defaults.settings.main += [
1212
"language-dictionaries.json",
1313
"password-recipes.json",
1414
"password-rules.json",
15+
"search-config-overrides-v2.json",
16+
"search-config-overrides.json",
1517
"search-config-v2.json",
1618
"search-config.json",
1719
"search-default-override-allowlist.json",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"data": [],
3+
"timestamp": 1704379586890
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"data": [],
3+
"timestamp": 1704379586619
4+
}

toolkit/components/glean/metrics_index.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
firefox_desktop_pings = [
114114
"browser/components/newtab/pings.yaml",
115115
"browser/components/pocket/pings.yaml",
116+
"browser/components/search/pings.yaml",
116117
"browser/components/urlbar/pings.yaml",
117118
"toolkit/components/crashes/pings.yaml",
118119
"toolkit/components/telemetry/pings.yaml",

toolkit/components/search/SearchEngine.sys.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,10 @@ export class SearchEngine {
10081008
this._urls.push(trending);
10091009
}
10101010

1011+
if (configuration.clickUrl) {
1012+
this.clickUrl = configuration.clickUrl;
1013+
}
1014+
10111015
if (details.encoding) {
10121016
this._queryCharset = details.encoding;
10131017
}

toolkit/components/search/SearchEngineSelector.sys.mjs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ export class SearchEngineSelector {
2828
*/
2929
constructor(listener) {
3030
this._remoteConfig = lazy.RemoteSettings(lazy.SearchUtils.NEW_SETTINGS_KEY);
31+
this._remoteConfigOverrides = lazy.RemoteSettings(
32+
lazy.SearchUtils.NEW_SETTINGS_OVERRIDES_KEY
33+
);
3134
this._listenerAdded = false;
3235
this._onConfigurationUpdated = this._onConfigurationUpdated.bind(this);
36+
this._onConfigurationOverridesUpdated =
37+
this._onConfigurationOverridesUpdated.bind(this);
3338
this._changeListener = listener;
3439
}
3540

@@ -41,8 +46,13 @@ export class SearchEngineSelector {
4146
return this._getConfigurationPromise;
4247
}
4348

44-
this._configuration = await (this._getConfigurationPromise =
45-
this._getConfiguration());
49+
this._getConfigurationPromise = Promise.all([
50+
this._getConfiguration(),
51+
this._getConfigurationOverrides(),
52+
]);
53+
let remoteSettingsData = await this._getConfigurationPromise;
54+
this._configuration = remoteSettingsData[0];
55+
this._configurationOverrides = remoteSettingsData[1];
4656
delete this._getConfigurationPromise;
4757

4858
if (!this._configuration?.length) {
@@ -54,12 +64,24 @@ export class SearchEngineSelector {
5464

5565
if (!this._listenerAdded) {
5666
this._remoteConfig.on("sync", this._onConfigurationUpdated);
67+
this._remoteConfigOverrides.on(
68+
"sync",
69+
this._onConfigurationOverridesUpdated
70+
);
5771
this._listenerAdded = true;
5872
}
5973

6074
return this._configuration;
6175
}
6276

77+
/**
78+
* Used by tests to get the configuration overrides.
79+
*/
80+
async getEngineConfigurationOverrides() {
81+
await this.getEngineConfiguration();
82+
return this._configurationOverrides;
83+
}
84+
6385
/**
6486
* Obtains the configuration from remote settings. This includes
6587
* verifying the signature of the record within the database.
@@ -119,6 +141,42 @@ export class SearchEngineSelector {
119141
}
120142
}
121143

144+
/**
145+
* Handles updating of the configuration. Note that the search service is
146+
* only updated after a period where the user is observed to be idle.
147+
*
148+
* @param {object} options
149+
* The options object
150+
* @param {object} options.data
151+
* The data to update
152+
* @param {Array} options.data.current
153+
* The new configuration object
154+
*/
155+
_onConfigurationOverridesUpdated({ data: { current } }) {
156+
this._configurationOverrides = current;
157+
lazy.logConsole.debug("Search configuration overrides updated remotely");
158+
if (this._changeListener) {
159+
this._changeListener();
160+
}
161+
}
162+
163+
/**
164+
* Obtains the configuration overrides from remote settings.
165+
*
166+
* @returns {Array}
167+
* An array of objects in the database, or an empty array if none
168+
* could be obtained.
169+
*/
170+
async _getConfigurationOverrides() {
171+
let result = [];
172+
try {
173+
result = await this._remoteConfigOverrides.get();
174+
} catch (ex) {
175+
// This data is remote only, so we just return an empty array if it fails.
176+
}
177+
return result;
178+
}
179+
122180
/**
123181
* @param {object} options
124182
* The options object
@@ -205,6 +263,12 @@ export class SearchEngineSelector {
205263
engine = this.#deepCopyObject(engine, variant);
206264
}
207265

266+
for (let override of this._configurationOverrides) {
267+
if (override.identifier == engine.identifier) {
268+
engine = this.#deepCopyObject(engine, override);
269+
}
270+
}
271+
208272
engines.push(engine);
209273
}
210274

0 commit comments

Comments
 (0)