зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1656220 - Implement recording attributions for search engines. r=dao
Differential Revision: https://phabricator.services.mozilla.com/D87501
This commit is contained in:
Родитель
87e0ce2ae4
Коммит
0542586f06
|
@ -253,6 +253,7 @@ let ContentSearch = {
|
|||
}
|
||||
win.BrowserSearch.recordSearchInTelemetry(engine, data.healthReportKey, {
|
||||
selection: data.selection,
|
||||
url: submission.uri,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1313,7 +1313,8 @@ pref("prompts.tab_modal.enabled", true);
|
|||
pref("prompts.defaultModalType", 3);
|
||||
|
||||
pref("browser.topsites.useRemoteSetting", false);
|
||||
pref("browser.topsites.attributionURL", "");
|
||||
|
||||
pref("browser.partnerlink.attributionURL", "https://topsites.mozilla.io/cid/amzn_2020_a1");
|
||||
|
||||
// Whether to show tab level system prompts opened via nsIPrompt(Service) as
|
||||
// SubDialogs in the TabDialogBox (true) or as TabModalPrompt in the
|
||||
|
|
|
@ -4346,7 +4346,7 @@ const BrowserSearch = {
|
|||
csp,
|
||||
});
|
||||
|
||||
return engine;
|
||||
return { engine, url: submission.uri };
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -4356,7 +4356,7 @@ const BrowserSearch = {
|
|||
* BrowserSearch.loadSearch for the preferred API.
|
||||
*/
|
||||
async loadSearchFromContext(terms, usePrivate, triggeringPrincipal, csp) {
|
||||
let engine = await BrowserSearch._loadSearch(
|
||||
let { engine, url } = await BrowserSearch._loadSearch(
|
||||
terms,
|
||||
usePrivate && !PrivateBrowsingUtils.isWindowPrivate(window)
|
||||
? "window"
|
||||
|
@ -4369,7 +4369,7 @@ const BrowserSearch = {
|
|||
csp
|
||||
);
|
||||
if (engine) {
|
||||
BrowserSearch.recordSearchInTelemetry(engine, "contextmenu");
|
||||
BrowserSearch.recordSearchInTelemetry(engine, "contextmenu", { url });
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -4377,7 +4377,7 @@ const BrowserSearch = {
|
|||
* Perform a search initiated from the command line.
|
||||
*/
|
||||
async loadSearchFromCommandLine(terms, usePrivate, triggeringPrincipal, csp) {
|
||||
let engine = await BrowserSearch._loadSearch(
|
||||
let { engine, url } = await BrowserSearch._loadSearch(
|
||||
terms,
|
||||
"current",
|
||||
usePrivate,
|
||||
|
@ -4386,7 +4386,7 @@ const BrowserSearch = {
|
|||
csp
|
||||
);
|
||||
if (engine) {
|
||||
BrowserSearch.recordSearchInTelemetry(engine, "system");
|
||||
BrowserSearch.recordSearchInTelemetry(engine, "system", { url });
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -117,7 +117,8 @@ this.search = class extends ExtensionAPI {
|
|||
BrowserUsageTelemetry.recordSearch(
|
||||
tabbrowser,
|
||||
engine,
|
||||
"webextension"
|
||||
"webextension",
|
||||
{ url: submission.uri }
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -421,6 +421,7 @@
|
|||
isOneOff: aOneOff,
|
||||
isSuggestion: !aOneOff && telemetrySearchDetails,
|
||||
selection: telemetrySearchDetails,
|
||||
url: submission.uri,
|
||||
};
|
||||
BrowserSearch.recordSearchInTelemetry(engine, "searchbar", details);
|
||||
// null parameter below specifies HTML response for search
|
||||
|
|
|
@ -474,7 +474,7 @@ class UrlbarInput {
|
|||
selectedOneOff.engine,
|
||||
searchString
|
||||
);
|
||||
this._recordSearch(selectedOneOff.engine, event);
|
||||
this._recordSearch(selectedOneOff.engine, event, { url });
|
||||
|
||||
UrlbarUtils.addToFormHistory(
|
||||
this,
|
||||
|
@ -776,6 +776,7 @@ class UrlbarInput {
|
|||
isSuggestion: !!result.payload.suggestion,
|
||||
isFormHistory: result.source == UrlbarUtils.RESULT_SOURCE.HISTORY,
|
||||
alias: result.payload.keyword,
|
||||
url,
|
||||
};
|
||||
const engine = Services.search.getEngineByName(result.payload.engine);
|
||||
this._recordSearch(engine, event, actionDetails);
|
||||
|
@ -1825,6 +1826,8 @@ class UrlbarInput {
|
|||
* True if this query was initiated via a search alias.
|
||||
* @param {boolean} searchActionDetails.isFormHistory
|
||||
* True if this query was initiated from a form history result.
|
||||
* @param {string} searchActionDetails.url
|
||||
* The url this query was triggered with.
|
||||
*/
|
||||
_recordSearch(engine, event, searchActionDetails = {}) {
|
||||
const isOneOff = this.view.oneOffSearchButtons.maybeRecordTelemetry(event);
|
||||
|
|
|
@ -23,6 +23,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
PageActions: "resource:///modules/PageActions.jsm",
|
||||
PartnerLinkAttribution: "resource:///modules/PartnerLinkAttribution.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
SearchTelemetry: "resource:///modules/SearchTelemetry.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
|
@ -604,7 +605,11 @@ let BrowserUsageTelemetry = {
|
|||
this._handleSearchAction(engine, source, details);
|
||||
},
|
||||
|
||||
_recordSearch(engine, source, action = null) {
|
||||
_recordSearch(engine, url, source, action = null) {
|
||||
PartnerLinkAttribution.makeSearchEngineRequest(engine, url).catch(
|
||||
Cu.reportError
|
||||
);
|
||||
|
||||
let scalarKey = action ? "search_" + action : "search";
|
||||
Services.telemetry.keyedScalarAdd(
|
||||
"browser.engagement.navigation." + source,
|
||||
|
@ -626,15 +631,15 @@ let BrowserUsageTelemetry = {
|
|||
this._handleSearchAndUrlbar(engine, source, details);
|
||||
break;
|
||||
case "abouthome":
|
||||
this._recordSearch(engine, "about_home", "enter");
|
||||
this._recordSearch(engine, details.url, "about_home", "enter");
|
||||
break;
|
||||
case "newtab":
|
||||
this._recordSearch(engine, "about_newtab", "enter");
|
||||
this._recordSearch(engine, details.url, "about_newtab", "enter");
|
||||
break;
|
||||
case "contextmenu":
|
||||
case "system":
|
||||
case "webextension":
|
||||
this._recordSearch(engine, source);
|
||||
this._recordSearch(engine, details.url, source);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -665,27 +670,27 @@ let BrowserUsageTelemetry = {
|
|||
}
|
||||
|
||||
// If that's a legit one-off search signal, record it using the relative key.
|
||||
this._recordSearch(engine, sourceName, "oneoff");
|
||||
this._recordSearch(engine, details.url, sourceName, "oneoff");
|
||||
return;
|
||||
}
|
||||
|
||||
// The search was not a one-off. It was a search with the default search engine.
|
||||
if (details.isFormHistory) {
|
||||
// It came from a form history result.
|
||||
this._recordSearch(engine, sourceName, "formhistory");
|
||||
this._recordSearch(engine, details.url, sourceName, "formhistory");
|
||||
return;
|
||||
} else if (details.isSuggestion) {
|
||||
// It came from a suggested search, so count it as such.
|
||||
this._recordSearch(engine, sourceName, "suggestion");
|
||||
this._recordSearch(engine, details.url, sourceName, "suggestion");
|
||||
return;
|
||||
} else if (details.alias) {
|
||||
// This one came from a search that used an alias.
|
||||
this._recordSearch(engine, sourceName, "alias");
|
||||
this._recordSearch(engine, details.url, sourceName, "alias");
|
||||
return;
|
||||
}
|
||||
|
||||
// The search signal was generated by typing something and pressing enter.
|
||||
this._recordSearch(engine, sourceName, "enter");
|
||||
this._recordSearch(engine, details.url, sourceName, "enter");
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,17 +8,14 @@ Cu.importGlobalProperties(["fetch"]);
|
|||
|
||||
var EXPORTED_SYMBOLS = ["PartnerLinkAttribution"];
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Services",
|
||||
"resource://gre/modules/Services.jsm"
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Region",
|
||||
"resource://gre/modules/Region.jsm"
|
||||
);
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
Region: "resource://gre/modules/Region.jsm",
|
||||
});
|
||||
|
||||
var PartnerLinkAttribution = {
|
||||
async makeRequest({ targetURL, source }) {
|
||||
|
@ -34,7 +31,7 @@ var PartnerLinkAttribution = {
|
|||
|
||||
const attributionUrl = Services.prefs.getStringPref(
|
||||
Services.prefs.getBoolPref("browser.topsites.useRemoteSetting")
|
||||
? "browser.topsites.attributionURL"
|
||||
? "browser.partnerlink.attributionURL"
|
||||
: `browser.newtabpage.searchTileOverride.${partner}.attributionURL`,
|
||||
""
|
||||
);
|
||||
|
@ -42,15 +39,67 @@ var PartnerLinkAttribution = {
|
|||
record("attribution", "abort");
|
||||
return;
|
||||
}
|
||||
const request = new Request(attributionUrl);
|
||||
request.headers.set("X-Region", Region.home);
|
||||
request.headers.set("X-Source", source);
|
||||
request.headers.set("X-Target-URL", targetURL);
|
||||
const response = await fetch(request);
|
||||
record("attribution", response.ok ? "success" : "failure");
|
||||
let result = await sendRequest(attributionUrl, source, targetURL);
|
||||
record("attribution", result ? "success" : "failure");
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes a request to the attribution URL for a search engine search.
|
||||
*
|
||||
* @param {nsISearchEngine} engine
|
||||
* The search engine to save the attribution for.
|
||||
* @param {nsIURI} targetUrl
|
||||
* The target URL to filter and include in the attribution.
|
||||
*/
|
||||
async makeSearchEngineRequest(engine, targetUrl) {
|
||||
if (!engine.sendAttributionRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
let searchUrlQueryParamName = engine.searchUrlQueryParamName;
|
||||
if (!searchUrlQueryParamName) {
|
||||
Cu.reportError("makeSearchEngineRequest can't find search terms key");
|
||||
return;
|
||||
}
|
||||
|
||||
let url = targetUrl;
|
||||
if (typeof url == "string") {
|
||||
url = Services.io.newURI(url);
|
||||
}
|
||||
|
||||
let targetParams = new URLSearchParams(url.query);
|
||||
if (!targetParams.has(searchUrlQueryParamName)) {
|
||||
Cu.reportError(
|
||||
"makeSearchEngineRequest can't remove target search terms"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const attributionUrl = Services.prefs.getStringPref(
|
||||
"browser.partnerlink.attributionURL",
|
||||
""
|
||||
);
|
||||
|
||||
targetParams.delete(searchUrlQueryParamName);
|
||||
let strippedTargetUrl = `${url.prePath}${url.filePath}`;
|
||||
let newParams = targetParams.toString();
|
||||
if (newParams) {
|
||||
strippedTargetUrl += "?" + newParams;
|
||||
}
|
||||
|
||||
await sendRequest(attributionUrl, "searchurl", strippedTargetUrl);
|
||||
},
|
||||
};
|
||||
|
||||
async function sendRequest(attributionUrl, source, targetURL) {
|
||||
const request = new Request(attributionUrl);
|
||||
request.headers.set("X-Region", Region.home);
|
||||
request.headers.set("X-Source", source);
|
||||
request.headers.set("X-Target-URL", targetURL);
|
||||
const response = await fetch(request);
|
||||
return response.ok;
|
||||
}
|
||||
|
||||
function recordTelemetryEvent(method, objectString, value, extra) {
|
||||
Services.telemetry.setEventRecordingEnabled("partner_link", true);
|
||||
Services.telemetry.recordEvent(
|
||||
|
|
|
@ -15,10 +15,15 @@ support-files =
|
|||
!/browser/components/search/test/browser/testEngine.xml
|
||||
!/browser/components/search/test/browser/testEngine_diacritics.xml
|
||||
testEngine_chromeicon.xml
|
||||
skip-if = (debug && os == "linux" && bits == 64 && os_version == "18.04") # Bug 1649755
|
||||
skip-if = (debug && os == "linux" && bits == 64 && os_version == "18.04") # Bug 1649755
|
||||
[browser_EveryWindow.js]
|
||||
[browser_LiveBookmarkMigrator.js]
|
||||
[browser_PageActions.js]
|
||||
[browser_PartnerLinkAttribution.js]
|
||||
support-files =
|
||||
search-engines/basic/manifest.json
|
||||
search-engines/simple/manifest.json
|
||||
search-engines/engines.json
|
||||
[browser_PermissionUI.js]
|
||||
[browser_PermissionUI_prompts.js]
|
||||
[browser_preloading_tab_moving.js]
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file tests urlbar telemetry with search related actions.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const SCALAR_URLBAR = "browser.engagement.navigation.urlbar";
|
||||
|
||||
// The preference to enable suggestions in the urlbar.
|
||||
const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
|
||||
// The name of the search engine used to generate suggestions.
|
||||
const SUGGESTION_ENGINE_NAME =
|
||||
"browser_UsageTelemetry usageTelemetrySearchSuggestions.xml";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
CustomizableUITestUtils:
|
||||
"resource://testing-common/CustomizableUITestUtils.jsm",
|
||||
Region: "resource://gre/modules/Region.jsm",
|
||||
SearchTelemetry: "resource:///modules/SearchTelemetry.jsm",
|
||||
SearchTestUtils: "resource://testing-common/SearchTestUtils.jsm",
|
||||
UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
|
||||
HttpServer: "resource://testing-common/httpd.js",
|
||||
});
|
||||
|
||||
let gCUITestUtils = new CustomizableUITestUtils(window);
|
||||
SearchTestUtils.init(Assert, registerCleanupFunction);
|
||||
|
||||
var gHttpServer = null;
|
||||
var gRequests = [];
|
||||
|
||||
function submitHandler(request, response) {
|
||||
gRequests.push(request);
|
||||
response.setStatusLine(request.httpVersion, 200, "Ok");
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
// Ensure the initial init is complete.
|
||||
await Services.search.init();
|
||||
|
||||
// Clear history so that history added by previous tests doesn't mess up this
|
||||
// test when it selects results in the urlbar.
|
||||
await PlacesUtils.history.clear();
|
||||
|
||||
let searchExtensions = getChromeDir(getResolvedURI(gTestPath));
|
||||
searchExtensions.append("search-engines");
|
||||
|
||||
await SearchTestUtils.useMochitestEngines(searchExtensions);
|
||||
|
||||
SearchTestUtils.useMockIdleService();
|
||||
let response = await fetch(`resource://search-extensions/engines.json`);
|
||||
let json = await response.json();
|
||||
await SearchTestUtils.updateRemoteSettingsConfig(json.data);
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.registerPathHandler("/", submitHandler);
|
||||
gHttpServer.start(-1);
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
// Enable search suggestions in the urlbar.
|
||||
[SUGGEST_URLBAR_PREF, true],
|
||||
// Clear historical search suggestions to avoid interference from previous
|
||||
// tests.
|
||||
["browser.urlbar.maxHistoricalSearchSuggestions", 0],
|
||||
// Use the default matching bucket configuration.
|
||||
["browser.urlbar.matchBuckets", "general:5,suggestion:4"],
|
||||
//
|
||||
[
|
||||
"browser.partnerlink.attributionURL",
|
||||
`http://localhost:${gHttpServer.identity.primaryPort}/`,
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
await gCUITestUtils.addSearchBar();
|
||||
|
||||
// Make sure to restore the engine once we're done.
|
||||
registerCleanupFunction(async function() {
|
||||
await SearchTestUtils.updateRemoteSettingsConfig();
|
||||
await gHttpServer.stop();
|
||||
gHttpServer = null;
|
||||
await PlacesUtils.history.clear();
|
||||
gCUITestUtils.removeSearchBar();
|
||||
});
|
||||
});
|
||||
|
||||
function searchInAwesomebar(value, win = window) {
|
||||
return UrlbarTestUtils.promiseAutocompleteResultPopup({
|
||||
window: win,
|
||||
waitForFocus,
|
||||
value,
|
||||
fireInputEvent: true,
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_simpleQuery_no_attribution() {
|
||||
await Services.search.setDefault(
|
||||
Services.search.getEngineByName("Simple Engine")
|
||||
);
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
"about:blank"
|
||||
);
|
||||
|
||||
info("Simulate entering a simple search.");
|
||||
let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt(
|
||||
"https://example.com/?sourceId=Mozilla-search&search=simple+query",
|
||||
tab
|
||||
);
|
||||
await searchInAwesomebar("simple query");
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
await promiseLoad;
|
||||
|
||||
await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
|
||||
|
||||
Assert.equal(gRequests.length, 0, "Should not have submitted an attribution");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
await Services.search.setDefault(Services.search.getEngineByName("basic"));
|
||||
});
|
||||
|
||||
async function checkAttributionRecorded(actionFn, cleanupFn) {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
"data:text/plain;charset=utf8,simple%20query"
|
||||
);
|
||||
|
||||
let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt(
|
||||
"https://mochi.test:8888/browser/browser/components/search/test/browser/?search=simple+query&foo=1",
|
||||
tab
|
||||
);
|
||||
await actionFn(tab);
|
||||
await promiseLoad;
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => gRequests.length == 1,
|
||||
"Should have received an attribution submission"
|
||||
);
|
||||
Assert.equal(
|
||||
gRequests[0].getHeader("x-region"),
|
||||
Region.home,
|
||||
"Should have set the region correctly"
|
||||
);
|
||||
Assert.equal(
|
||||
gRequests[0].getHeader("X-Source"),
|
||||
"searchurl",
|
||||
"Should have set the source correctly"
|
||||
);
|
||||
Assert.equal(
|
||||
gRequests[0].getHeader("X-Target-url"),
|
||||
"https://mochi.test:8888/browser/browser/components/search/test/browser/?foo=1",
|
||||
"Should have set the target url correctly and stripped the search terms"
|
||||
);
|
||||
if (cleanupFn) {
|
||||
await cleanupFn();
|
||||
}
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
gRequests = [];
|
||||
}
|
||||
|
||||
add_task(async function test_urlbar() {
|
||||
await checkAttributionRecorded(async tab => {
|
||||
await searchInAwesomebar("simple query");
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_searchbar() {
|
||||
await checkAttributionRecorded(async tab => {
|
||||
let sb = BrowserSearch.searchBar;
|
||||
// Write the search query in the searchbar.
|
||||
sb.focus();
|
||||
sb.value = "simple query";
|
||||
sb.textbox.controller.startSearch("simple query");
|
||||
// Wait for the popup to show.
|
||||
await BrowserTestUtils.waitForEvent(sb.textbox.popup, "popupshown");
|
||||
// And then for the search to complete.
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() =>
|
||||
sb.textbox.controller.searchStatus >=
|
||||
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH,
|
||||
"The search in the searchbar must complete."
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_context_menu() {
|
||||
let contextMenu;
|
||||
await checkAttributionRecorded(
|
||||
async tab => {
|
||||
info("Select all the text in the page.");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [""], async function() {
|
||||
return new Promise(resolve => {
|
||||
content.document.addEventListener(
|
||||
"selectionchange",
|
||||
() => resolve(),
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
content.document
|
||||
.getSelection()
|
||||
.selectAllChildren(content.document.body);
|
||||
});
|
||||
});
|
||||
|
||||
info("Open the context menu.");
|
||||
contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let popupPromise = BrowserTestUtils.waitForEvent(
|
||||
contextMenu,
|
||||
"popupshown"
|
||||
);
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"body",
|
||||
{ type: "contextmenu", button: 2 },
|
||||
gBrowser.selectedBrowser
|
||||
);
|
||||
await popupPromise;
|
||||
|
||||
info("Click on search.");
|
||||
let searchItem = contextMenu.getElementsByAttribute(
|
||||
"id",
|
||||
"context-searchselect"
|
||||
)[0];
|
||||
searchItem.click();
|
||||
},
|
||||
() => {
|
||||
contextMenu.hidePopup();
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_about_newtab() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
"about:newtab",
|
||||
false
|
||||
);
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
|
||||
await ContentTaskUtils.waitForCondition(() => !content.document.hidden);
|
||||
});
|
||||
|
||||
info("Trigger a simple serch, just text + enter.");
|
||||
let promiseLoad = BrowserTestUtils.waitForDocLoadAndStopIt(
|
||||
"https://mochi.test:8888/browser/browser/components/search/test/browser/?search=simple+query&foo=1",
|
||||
tab
|
||||
);
|
||||
await typeInSearchField(
|
||||
tab.linkedBrowser,
|
||||
"simple query",
|
||||
"newtab-search-text"
|
||||
);
|
||||
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser);
|
||||
await promiseLoad;
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => gRequests.length == 1,
|
||||
"Should have received an attribution submission"
|
||||
);
|
||||
Assert.equal(
|
||||
gRequests[0].getHeader("x-region"),
|
||||
Region.home,
|
||||
"Should have set the region correctly"
|
||||
);
|
||||
Assert.equal(
|
||||
gRequests[0].getHeader("X-Source"),
|
||||
"searchurl",
|
||||
"Should have set the source correctly"
|
||||
);
|
||||
Assert.equal(
|
||||
gRequests[0].getHeader("X-Target-url"),
|
||||
"https://mochi.test:8888/browser/browser/components/search/test/browser/?foo=1",
|
||||
"Should have set the target url correctly and stripped the search terms"
|
||||
);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
gRequests = [];
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "basic",
|
||||
"manifest_version": 2,
|
||||
"version": "1.0",
|
||||
"description": "basic",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "basic@search.mozilla.org"
|
||||
}
|
||||
},
|
||||
"hidden": true,
|
||||
"chrome_settings_overrides": {
|
||||
"search_provider": {
|
||||
"name": "basic",
|
||||
"search_url": "https://mochi.test:8888/browser/browser/components/search/test/browser/?search={searchTerms}&foo=1",
|
||||
"suggest_url": "https://mochi.test:8888/browser/browser/modules/test/browser/usageTelemetrySearchSuggestions.sjs?{searchTerms}"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"webExtension": {
|
||||
"id":"basic@search.mozilla.org"
|
||||
},
|
||||
"telemetryId": "telemetry",
|
||||
"appliesTo": [{
|
||||
"included": { "everywhere": true },
|
||||
"default": "yes",
|
||||
"sendAttributionRequest": true
|
||||
}]
|
||||
},
|
||||
{
|
||||
"webExtension": {
|
||||
"id":"simple@search.mozilla.org"
|
||||
},
|
||||
"appliesTo": [{
|
||||
"included": { "everywhere": true },
|
||||
"default": "yes"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "Simple Engine",
|
||||
"manifest_version": 2,
|
||||
"version": "1.0",
|
||||
"description": "Simple engine with a different name from the WebExtension id prefix",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "simple@search.mozilla.org"
|
||||
}
|
||||
},
|
||||
"hidden": true,
|
||||
"chrome_settings_overrides": {
|
||||
"search_provider": {
|
||||
"name": "Simple Engine",
|
||||
"search_url": "https://example.com",
|
||||
"params": [
|
||||
{
|
||||
"name": "sourceId",
|
||||
"value": "Mozilla-search"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"value": "{searchTerms}"
|
||||
}
|
||||
],
|
||||
"suggest_url": "https://example.com?search={searchTerms}"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -622,6 +622,9 @@ class SearchEngine {
|
|||
_definedAliases = [];
|
||||
// The urls associated with this engine.
|
||||
_urls = [];
|
||||
// The query parameter name of the search url, cached in memory to avoid
|
||||
// repeated look-ups.
|
||||
_searchUrlQueryParamName = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -1506,6 +1509,32 @@ class SearchEngine {
|
|||
return url.getSubmission(submissionData, this, purpose);
|
||||
}
|
||||
|
||||
get searchUrlQueryParamName() {
|
||||
if (this._searchUrlQueryParamName != null) {
|
||||
return this._searchUrlQueryParamName;
|
||||
}
|
||||
|
||||
let submission = this.getSubmission(
|
||||
"{searchTerms}",
|
||||
SearchUtils.URL_TYPE.SEARCH
|
||||
);
|
||||
|
||||
if (submission.postData) {
|
||||
Cu.reportError("searchUrlQueryParamName can't handle POST urls.");
|
||||
return (this._searchUrlQueryParamName = "");
|
||||
}
|
||||
|
||||
let queryParams = new URLSearchParams(submission.uri.query);
|
||||
let searchUrlQueryParamName = "";
|
||||
for (let [key, value] of queryParams) {
|
||||
if (value == "{searchTerms}") {
|
||||
searchUrlQueryParamName = key;
|
||||
}
|
||||
}
|
||||
|
||||
return (this._searchUrlQueryParamName = searchUrlQueryParamName);
|
||||
}
|
||||
|
||||
// from nsISearchEngine
|
||||
supportsResponseType(type) {
|
||||
return this._getURLOfType(type) != null;
|
||||
|
|
|
@ -50,6 +50,15 @@ interface nsISearchEngine : nsISupports
|
|||
[optional] in AString responseType,
|
||||
[optional] in AString purpose);
|
||||
|
||||
/**
|
||||
* Returns the name of the parameter used for the search terms for a submission
|
||||
* URL of type `SearchUtils.URL_TYPE.SEARCH`.
|
||||
*
|
||||
* @returns A string which is the name of the parameter, or empty string
|
||||
* if no parameter cannot be found or is not supported (e.g. POST).
|
||||
*/
|
||||
readonly attribute AString searchUrlQueryParamName;
|
||||
|
||||
/**
|
||||
* Determines whether the engine can return responses in the given
|
||||
* MIME type. Returns true if the engine spec has a URL with the
|
||||
|
|
|
@ -108,6 +108,22 @@ var SearchTestUtils = Object.freeze({
|
|||
}
|
||||
},
|
||||
|
||||
async useMochitestEngines(testDir) {
|
||||
// Replace the path we load search engines from with
|
||||
// the path to our test data.
|
||||
let resProt = Services.io
|
||||
.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
let originalSubstitution = resProt.getSubstitution("search-extensions");
|
||||
resProt.setSubstitution(
|
||||
"search-extensions",
|
||||
Services.io.newURI("file://" + testDir.path)
|
||||
);
|
||||
gTestGlobals.registerCleanupFunction(() => {
|
||||
resProt.setSubstitution("search-extensions", originalSubstitution);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a list of engine configurations into engine objects.
|
||||
*
|
||||
|
@ -284,10 +300,14 @@ var SearchTestUtils = Object.freeze({
|
|||
/**
|
||||
* Simulates an update to the RemoteSettings configuration.
|
||||
*
|
||||
* @param {object} config
|
||||
* @param {object} [config]
|
||||
* The new configuration.
|
||||
*/
|
||||
async updateRemoteSettingsConfig(config) {
|
||||
if (!config) {
|
||||
let settings = RemoteSettings(SearchUtils.SETTINGS_KEY);
|
||||
config = await settings.get();
|
||||
}
|
||||
const reloadObserved = SearchTestUtils.promiseSearchNotification(
|
||||
"engines-reloaded"
|
||||
);
|
||||
|
|
Загрузка…
Ссылка в новой задаче