зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 396da163d87a (bug 1627541) for perma failures on test_geodefaults.js. CLOSED TREE
This commit is contained in:
Родитель
70ab539dc0
Коммит
f2ef387aa5
|
@ -116,10 +116,35 @@ const MULTI_LOCALE_ENGINES = [
|
|||
"multilocale",
|
||||
];
|
||||
|
||||
// A method that tries to determine if this user is in a US geography.
|
||||
function isUSTimezone() {
|
||||
// Timezone assumptions! We assume that if the system clock's timezone is
|
||||
// between Newfoundland and Hawaii, that the user is in North America.
|
||||
|
||||
// This includes all of South America as well, but we have relatively few
|
||||
// en-US users there, so that's OK.
|
||||
|
||||
// 150 minutes = 2.5 hours (UTC-2.5), which is
|
||||
// Newfoundland Daylight Time (http://www.timeanddate.com/time/zones/ndt)
|
||||
|
||||
// 600 minutes = 10 hours (UTC-10), which is
|
||||
// Hawaii-Aleutian Standard Time (http://www.timeanddate.com/time/zones/hast)
|
||||
|
||||
let UTCOffset = new Date().getTimezoneOffset();
|
||||
return UTCOffset >= 150 && UTCOffset <= 600;
|
||||
}
|
||||
|
||||
// A method that tries to determine our region via an XHR geoip lookup.
|
||||
var ensureKnownRegion = async function(ss) {
|
||||
var ensureKnownRegion = async function(ss, awaitRegionCheck) {
|
||||
// If we have a region already stored in our prefs we trust it.
|
||||
let region = Services.prefs.getCharPref("browser.search.region", "");
|
||||
try {
|
||||
if (gGeoSpecificDefaultsEnabled && !gModernConfig) {
|
||||
if (gGeoSpecificDefaultsEnabled && !region) {
|
||||
// We don't have it cached, so fetch it. fetchRegion() will call
|
||||
// storeRegion if it gets a result (even if that happens after the
|
||||
// promise resolves) and fetchRegionDefault.
|
||||
await fetchRegion(ss, awaitRegionCheck);
|
||||
} else if (gGeoSpecificDefaultsEnabled && !gModernConfig) {
|
||||
// The territory default we have already fetched may have expired.
|
||||
let expired = (ss.getGlobalAttr("searchDefaultExpir") || 0) <= Date.now();
|
||||
// If we have a default engine or a list of visible default engines
|
||||
|
@ -146,7 +171,7 @@ var ensureKnownRegion = async function(ss) {
|
|||
clearTimeout(timerId);
|
||||
resolve();
|
||||
};
|
||||
fetchRegionDefault(ss)
|
||||
fetchRegionDefault(ss, awaitRegionCheck)
|
||||
.then(callback)
|
||||
.catch(err => {
|
||||
Cu.reportError(err);
|
||||
|
@ -169,6 +194,117 @@ var ensureKnownRegion = async function(ss) {
|
|||
}
|
||||
};
|
||||
|
||||
// Store the result of the geoip request as well as any other values and
|
||||
// telemetry which depend on it.
|
||||
async function storeRegion(region) {
|
||||
let isTimezoneUS = isUSTimezone();
|
||||
// If it's a US region, but not a US timezone, we don't store the value.
|
||||
// This works because no region defaults to ZZ (unknown) in nsURLFormatter
|
||||
if (region != "US" || isTimezoneUS) {
|
||||
Services.prefs.setCharPref("browser.search.region", region);
|
||||
}
|
||||
|
||||
// and telemetry...
|
||||
if (region == "US" && !isTimezoneUS) {
|
||||
SearchUtils.log("storeRegion mismatch - US Region, non-US timezone");
|
||||
Services.telemetry
|
||||
.getHistogramById("SEARCH_SERVICE_US_COUNTRY_MISMATCHED_TIMEZONE")
|
||||
.add(1);
|
||||
}
|
||||
if (region != "US" && isTimezoneUS) {
|
||||
SearchUtils.log("storeRegion mismatch - non-US Region, US timezone");
|
||||
Services.telemetry
|
||||
.getHistogramById("SEARCH_SERVICE_US_TIMEZONE_MISMATCHED_COUNTRY")
|
||||
.add(1);
|
||||
}
|
||||
// telemetry to compare our geoip response with platform-specific country data.
|
||||
// On Mac and Windows, we can get a country code via sysinfo
|
||||
let platformCC = await Services.sysinfo.countryCode;
|
||||
if (platformCC) {
|
||||
let probeUSMismatched, probeNonUSMismatched;
|
||||
switch (AppConstants.platform) {
|
||||
case "macosx":
|
||||
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX";
|
||||
probeNonUSMismatched =
|
||||
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX";
|
||||
break;
|
||||
case "win":
|
||||
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN";
|
||||
probeNonUSMismatched =
|
||||
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN";
|
||||
break;
|
||||
default:
|
||||
Cu.reportError(
|
||||
"Platform " +
|
||||
Services.appinfo.OS +
|
||||
" has system country code but no search service telemetry probes"
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (probeUSMismatched && probeNonUSMismatched) {
|
||||
if (region == "US" || platformCC == "US") {
|
||||
// one of the 2 said US, so record if they are the same.
|
||||
Services.telemetry
|
||||
.getHistogramById(probeUSMismatched)
|
||||
.add(region != platformCC);
|
||||
} else {
|
||||
// non-US - record if they are the same
|
||||
Services.telemetry
|
||||
.getHistogramById(probeNonUSMismatched)
|
||||
.add(region != platformCC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the region we are in via a XHR geoip request.
|
||||
async function fetchRegion(ss, awaitRegionCheck) {
|
||||
// values for the SEARCH_SERVICE_COUNTRY_FETCH_RESULT 'enum' telemetry probe.
|
||||
const TELEMETRY_RESULT_ENUM = {
|
||||
success: 0,
|
||||
"region-fetch-no-result": 1,
|
||||
"region-fetch-timeout": 2,
|
||||
error: 3,
|
||||
};
|
||||
let startTime = Date.now();
|
||||
let result;
|
||||
let telemetryResult = TELEMETRY_RESULT_ENUM.success;
|
||||
try {
|
||||
result = await Region.getHomeRegion();
|
||||
} catch (err) {
|
||||
telemetryResult =
|
||||
TELEMETRY_RESULT_ENUM[err.message] || TELEMETRY_RESULT_ENUM.error;
|
||||
Cu.reportError(err);
|
||||
}
|
||||
let took = Date.now() - startTime;
|
||||
|
||||
if (result?.country_code) {
|
||||
await storeRegion(result.country_code).catch(Cu.reportError);
|
||||
}
|
||||
SearchUtils.log(
|
||||
"_fetchRegion got success response in " + took + "ms: " + result
|
||||
);
|
||||
Services.telemetry
|
||||
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS")
|
||||
.add(took);
|
||||
|
||||
// Now that we know the current region, it's possible to fetch defaults,
|
||||
// which we couldn't do before in `ensureKnownRegion`.
|
||||
try {
|
||||
if (result && gModernConfig) {
|
||||
await ss._maybeReloadEngines(awaitRegionCheck);
|
||||
} else if (result && !gModernConfig) {
|
||||
await fetchRegionDefault(ss, awaitRegionCheck);
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
|
||||
Services.telemetry
|
||||
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_RESULT")
|
||||
.add(telemetryResult);
|
||||
}
|
||||
|
||||
// This converts our legacy google engines to the
|
||||
// new codes. We have to manually change them here
|
||||
// because we can't change the default name in absearch.
|
||||
|
@ -199,7 +335,7 @@ function convertGoogleEngines(engineNames) {
|
|||
// responsibility to ensure with a timer that we are not going to
|
||||
// block the async init for too long.
|
||||
// @deprecated Unused in the modern config.
|
||||
var fetchRegionDefault = ss =>
|
||||
var fetchRegionDefault = (ss, awaitRegionCheck) =>
|
||||
new Promise(resolve => {
|
||||
let urlTemplate = Services.prefs
|
||||
.getDefaultBranch(SearchUtils.BROWSER_SEARCH_PREF)
|
||||
|
@ -276,7 +412,7 @@ var fetchRegionDefault = ss =>
|
|||
);
|
||||
// If we're doing this somewhere during the app's lifetime, reload the list
|
||||
// of engines in order to pick up any geo-specific changes.
|
||||
ss._maybeReloadEngines().finally(resolve);
|
||||
ss._maybeReloadEngines(awaitRegionCheck).finally(resolve);
|
||||
};
|
||||
request.ontimeout = function(event) {
|
||||
SearchUtils.log("fetchRegionDefault: XHR finally timed-out");
|
||||
|
@ -527,10 +663,15 @@ SearchService.prototype = {
|
|||
/**
|
||||
* Asynchronous implementation of the initializer.
|
||||
*
|
||||
* @param {boolean} [awaitRegionCheck]
|
||||
* Indicates whether we should explicitly await the the region check process to
|
||||
* complete, which may be fetched remotely. Pass in `true` if the caller needs
|
||||
* to be absolutely certain of the correct default engine and/ or ordering of
|
||||
* visible engines.
|
||||
* @returns {number}
|
||||
* A Components.results success code on success, otherwise a failure code.
|
||||
*/
|
||||
async _init() {
|
||||
async _init(awaitRegionCheck) {
|
||||
SearchUtils.log("_init start");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
|
@ -549,11 +690,6 @@ SearchService.prototype = {
|
|||
this._onSeparateDefaultPrefChanged.bind(this)
|
||||
);
|
||||
|
||||
// We need to catch the region being updated
|
||||
// during initialisation so we start listening
|
||||
// straight away.
|
||||
Services.obs.addObserver(this, Region.REGION_TOPIC);
|
||||
|
||||
try {
|
||||
if (gModernConfig) {
|
||||
// Create the search engine selector.
|
||||
|
@ -568,11 +704,14 @@ SearchService.prototype = {
|
|||
// The init flow is not going to block on a fetch from an external service,
|
||||
// but we're kicking it off as soon as possible to prevent UI flickering as
|
||||
// much as possible.
|
||||
this._ensureKnownRegionPromise = ensureKnownRegion(this)
|
||||
this._ensureKnownRegionPromise = ensureKnownRegion(this, awaitRegionCheck)
|
||||
.catch(ex =>
|
||||
SearchUtils.log("_init: failure determining region: " + ex)
|
||||
)
|
||||
.finally(() => (this._ensureKnownRegionPromise = null));
|
||||
if (awaitRegionCheck) {
|
||||
await this._ensureKnownRegionPromise;
|
||||
}
|
||||
|
||||
this._setupRemoteSettings().catch(Cu.reportError);
|
||||
|
||||
|
@ -1319,10 +1458,17 @@ SearchService.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Reloads engines asynchronously, but only when
|
||||
* the service has already been initialized.
|
||||
* Reloads engines asynchronously, but only when the service has already been
|
||||
* initialized.
|
||||
* @param {boolean} awaitRegionCheck
|
||||
* Whether the caller is waiting for the region check.
|
||||
*/
|
||||
async _maybeReloadEngines() {
|
||||
async _maybeReloadEngines(awaitRegionCheck) {
|
||||
// The caller is already awaiting on the region check
|
||||
// completing, so we dont need to queue a reload.
|
||||
if (awaitRegionCheck) {
|
||||
return;
|
||||
}
|
||||
if (!gInitialized) {
|
||||
if (this._maybeReloadDebounce) {
|
||||
SearchUtils.log(
|
||||
|
@ -1408,8 +1554,8 @@ SearchService.prototype = {
|
|||
);
|
||||
},
|
||||
|
||||
_reInit(origin) {
|
||||
SearchUtils.log("_reInit");
|
||||
_reInit(origin, awaitRegionCheck = false) {
|
||||
SearchUtils.log("_reInit: " + awaitRegionCheck);
|
||||
// Re-entrance guard, because we're using an async lambda below.
|
||||
if (gReinitializing) {
|
||||
SearchUtils.log("_reInit: already re-initializing, bailing out.");
|
||||
|
@ -1466,12 +1612,19 @@ SearchService.prototype = {
|
|||
// The init flow is not going to block on a fetch from an external service,
|
||||
// but we're kicking it off as soon as possible to prevent UI flickering as
|
||||
// much as possible.
|
||||
this._ensureKnownRegionPromise = ensureKnownRegion(this)
|
||||
this._ensureKnownRegionPromise = ensureKnownRegion(
|
||||
this,
|
||||
awaitRegionCheck
|
||||
)
|
||||
.catch(ex =>
|
||||
SearchUtils.log("_reInit: failure determining region: " + ex)
|
||||
)
|
||||
.finally(() => (this._ensureKnownRegionPromise = null));
|
||||
|
||||
if (awaitRegionCheck) {
|
||||
await this._ensureKnownRegionPromise;
|
||||
}
|
||||
|
||||
await this._loadEngines(cache);
|
||||
|
||||
// If we've got this far, but the application is now shutting down,
|
||||
|
@ -1765,7 +1918,7 @@ SearchService.prototype = {
|
|||
SearchUtils.log("_findEngineSelectorEngines: init");
|
||||
|
||||
let locale = Services.locale.appLocaleAsBCP47;
|
||||
let region = Region.home || "default";
|
||||
let region = Services.prefs.getCharPref("browser.search.region", "default");
|
||||
|
||||
let channel = AppConstants.MOZ_APP_VERSION_DISPLAY.endsWith("esr")
|
||||
? "esr"
|
||||
|
@ -1851,7 +2004,7 @@ SearchService.prototype = {
|
|||
* @returns {Array<string>}
|
||||
* Returns an array of engine names.
|
||||
*/
|
||||
async _parseListJSON(list) {
|
||||
_parseListJSON(list) {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(list);
|
||||
|
@ -1861,7 +2014,10 @@ SearchService.prototype = {
|
|||
return [];
|
||||
}
|
||||
|
||||
let searchRegion = Region.home;
|
||||
let searchRegion = Services.prefs.getCharPref(
|
||||
"browser.search.region",
|
||||
null
|
||||
);
|
||||
|
||||
let searchSettings;
|
||||
let locale = Services.locale.appLocaleAsBCP47;
|
||||
|
@ -2249,9 +2405,12 @@ SearchService.prototype = {
|
|||
},
|
||||
|
||||
// nsISearchService
|
||||
async init() {
|
||||
SearchUtils.log("SearchService.init");
|
||||
async init(awaitRegionCheck = false) {
|
||||
SearchUtils.log("SearchService.init: " + awaitRegionCheck);
|
||||
if (this._initStarted) {
|
||||
if (awaitRegionCheck) {
|
||||
await this._ensureKnownRegionPromise;
|
||||
}
|
||||
return this._initObservers.promise;
|
||||
}
|
||||
|
||||
|
@ -2259,7 +2418,7 @@ SearchService.prototype = {
|
|||
this._initStarted = true;
|
||||
try {
|
||||
// Complete initialization by calling asynchronous initializer.
|
||||
await this._init();
|
||||
await this._init(awaitRegionCheck);
|
||||
TelemetryStopwatch.finish("SEARCH_SERVICE_INIT_MS");
|
||||
} catch (ex) {
|
||||
if (ex.result == Cr.NS_ERROR_ALREADY_INITIALIZED) {
|
||||
|
@ -2286,8 +2445,8 @@ SearchService.prototype = {
|
|||
},
|
||||
|
||||
// reInit is currently only exposed for testing purposes
|
||||
async reInit() {
|
||||
return this._reInit("test");
|
||||
async reInit(awaitRegionCheck) {
|
||||
return this._reInit("test", awaitRegionCheck);
|
||||
},
|
||||
|
||||
async getEngines() {
|
||||
|
@ -3513,14 +3672,6 @@ SearchService.prototype = {
|
|||
}
|
||||
});
|
||||
break;
|
||||
case Region.REGION_TOPIC:
|
||||
if (verb == Region.REGION_UPDATED) {
|
||||
SearchUtils.log("Region updated: " + Region.home);
|
||||
ensureKnownRegion(this)
|
||||
.then(this._maybeReloadEngines.bind(this))
|
||||
.catch(Cu.reportError);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3653,7 +3804,6 @@ SearchService.prototype = {
|
|||
Services.obs.removeObserver(this, SearchUtils.TOPIC_ENGINE_MODIFIED);
|
||||
Services.obs.removeObserver(this, QUIT_APPLICATION_TOPIC);
|
||||
Services.obs.removeObserver(this, TOPIC_LOCALES_CHANGE);
|
||||
Services.obs.removeObserver(this, Region.REGION_TOPIC);
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
|
|
|
@ -228,14 +228,20 @@ interface nsISearchService : nsISupports
|
|||
* immediately, if initialization has already been completed by some previous
|
||||
* call to this method.
|
||||
* This method should only be called when you need or want to wait for the
|
||||
* full initialization of the search service.
|
||||
* full initialization of the search service, which may include waiting for
|
||||
* outbound service requests.
|
||||
*
|
||||
* @param skipRegionCheck
|
||||
* Flag to avoid SearchService from waiting on the results
|
||||
* of the region lookups before completing initialisation.
|
||||
* Exposed for testing only.
|
||||
*/
|
||||
Promise init();
|
||||
Promise init([optional] in boolean skipRegionCheck);
|
||||
|
||||
/**
|
||||
* Exposed for testing.
|
||||
*/
|
||||
void reInit();
|
||||
void reInit([optional] in boolean skipRegionCheck);
|
||||
void reset();
|
||||
Promise makeEngineFromConfig(in jsval config);
|
||||
Promise ensureBuiltinExtension(in AString id,
|
||||
|
|
|
@ -9,7 +9,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
FileUtils: "resource://gre/modules/FileUtils.jsm",
|
||||
NetUtil: "resource://gre/modules/NetUtil.jsm",
|
||||
PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
|
||||
Region: "resource://gre/modules/Region.jsm",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.js",
|
||||
RemoteSettingsClient: "resource://services-settings/RemoteSettingsClient.jsm",
|
||||
SearchEngineSelector: "resource://gre/modules/SearchEngineSelector.jsm",
|
||||
|
@ -46,7 +45,6 @@ var XULRuntime = Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime);
|
|||
|
||||
// Expand the amount of information available in error logs
|
||||
Services.prefs.setBoolPref("browser.search.log", true);
|
||||
Services.prefs.setBoolPref("browser.region.log", true);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
|
@ -534,12 +532,14 @@ function installTestEngine() {
|
|||
}
|
||||
|
||||
async function asyncReInit({ awaitRegionFetch = false } = {}) {
|
||||
let promises = [
|
||||
SearchTestUtils.promiseSearchNotification("reinit-complete"),
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
];
|
||||
let promises = [SearchTestUtils.promiseSearchNotification("reinit-complete")];
|
||||
if (awaitRegionFetch) {
|
||||
promises.push(
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done")
|
||||
);
|
||||
}
|
||||
|
||||
Services.search.reInit();
|
||||
Services.search.reInit(awaitRegionFetch);
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
Region: "resource://gre/modules/Region.jsm",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.js",
|
||||
SearchEngine: "resource://gre/modules/SearchEngine.jsm",
|
||||
SearchEngineSelector: "resource://gre/modules/SearchEngineSelector.jsm",
|
||||
|
@ -215,11 +214,14 @@ class SearchConfigTest {
|
|||
* The two-letter locale code.
|
||||
*/
|
||||
async _reinit(region, locale) {
|
||||
Region._setRegion(region?.toUpperCase(), true);
|
||||
if (region) {
|
||||
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
|
||||
Services.prefs.setStringPref(
|
||||
"browser.search.region",
|
||||
region.toUpperCase()
|
||||
);
|
||||
} else {
|
||||
Services.prefs.clearUserPref("browser.search.region");
|
||||
}
|
||||
|
||||
const reinitCompletePromise = SearchTestUtils.promiseSearchNotification(
|
||||
"reinit-complete"
|
||||
);
|
||||
|
|
|
@ -18,6 +18,8 @@ add_task(async function no_request_if_prefed_off() {
|
|||
await Services.search.init(true);
|
||||
await promiseAfterCache();
|
||||
|
||||
checkNoRequest(requests);
|
||||
|
||||
// Install kTestEngineName and wait for it to reach the disk.
|
||||
await Promise.all([installTestEngine(), promiseAfterCache()]);
|
||||
|
||||
|
@ -42,8 +44,11 @@ add_task(async function should_get_geo_defaults_only_once() {
|
|||
// (Re)initializing the search service should trigger a request,
|
||||
// and set the default engine based on it.
|
||||
// Due to the previous initialization, we expect the region to already be set.
|
||||
Region._setRegion("FR", false);
|
||||
await Promise.all([asyncReInit(), promiseAfterCache()]);
|
||||
await Promise.all([
|
||||
asyncReInit({ awaitRegionFetch: true }),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
checkRequest(requests);
|
||||
Assert.equal((await Services.search.getDefault()).name, kTestEngineName);
|
||||
|
||||
// Verify the metadata was written correctly.
|
||||
|
@ -56,17 +61,33 @@ add_task(async function should_get_geo_defaults_only_once() {
|
|||
Assert.equal(metadata.searchDefaultHash.length, 44);
|
||||
|
||||
// The next restart shouldn't trigger a request.
|
||||
await asyncReInit();
|
||||
await asyncReInit({ awaitRegionFetch: true });
|
||||
checkNoRequest(requests);
|
||||
Assert.equal((await Services.search.getDefault()).name, kTestEngineName);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function should_request_when_region_not_set() {
|
||||
await withGeoServer(async function cont(requests) {
|
||||
Services.prefs.clearUserPref("browser.search.region");
|
||||
await Promise.all([
|
||||
asyncReInit({ awaitRegionFetch: true }),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
checkRequest(requests);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function should_recheck_if_interval_expired() {
|
||||
await withGeoServer(async function cont(requests) {
|
||||
await forceExpiration();
|
||||
|
||||
let date = Date.now();
|
||||
await Promise.all([asyncReInit(), promiseAfterCache()]);
|
||||
await Promise.all([
|
||||
asyncReInit({ awaitRegionFetch: true }),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
checkRequest(requests);
|
||||
|
||||
// Check that the expiration timestamp has been updated.
|
||||
let metadata = await promiseGlobalMetadata();
|
||||
|
@ -88,7 +109,10 @@ add_task(async function should_recheck_if_appversion_changed() {
|
|||
data.appVersion = "1";
|
||||
await promiseSaveCacheData(data);
|
||||
|
||||
await Promise.all([asyncReInit(), promiseAfterCache()]);
|
||||
await Promise.all([
|
||||
asyncReInit({ awaitRegionFetch: true }),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
checkRequest(requests);
|
||||
|
||||
// Check that the appVersion has been updated
|
||||
|
@ -114,7 +138,7 @@ add_task(async function should_recheck_when_broken_hash() {
|
|||
let unInitPromise = SearchTestUtils.promiseSearchNotification(
|
||||
"uninit-complete"
|
||||
);
|
||||
let reInitPromise = asyncReInit();
|
||||
let reInitPromise = asyncReInit({ awaitRegionFetch: true });
|
||||
await unInitPromise;
|
||||
|
||||
await reInitPromise;
|
||||
|
@ -158,7 +182,7 @@ add_task(async function should_remember_cohort_id() {
|
|||
// Trigger a new request.
|
||||
await forceExpiration();
|
||||
let commitPromise = promiseAfterCache();
|
||||
await asyncReInit();
|
||||
await asyncReInit({ awaitRegionFetch: true });
|
||||
checkRequest(requests);
|
||||
await commitPromise;
|
||||
|
||||
|
@ -178,7 +202,7 @@ add_task(async function should_remember_cohort_id() {
|
|||
// will remove it from the prefs due to the server no longer sending it.
|
||||
await forceExpiration();
|
||||
let commitPromise = promiseAfterCache();
|
||||
await asyncReInit();
|
||||
await asyncReInit({ awaitRegionFetch: true });
|
||||
checkRequest(requests, cohort);
|
||||
await commitPromise;
|
||||
Assert.equal(
|
||||
|
@ -193,14 +217,41 @@ add_task(async function should_retry_after_failure() {
|
|||
async function cont(requests) {
|
||||
// Trigger a new request.
|
||||
await forceExpiration();
|
||||
await asyncReInit();
|
||||
await asyncReInit({ awaitRegionFetch: true });
|
||||
checkRequest(requests);
|
||||
|
||||
// After another restart, a new request should be triggered automatically without
|
||||
// the test having to call forceExpiration again.
|
||||
await asyncReInit();
|
||||
await asyncReInit({ awaitRegionFetch: true });
|
||||
checkRequest(requests);
|
||||
},
|
||||
{ path: "lookup_fail" }
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function should_honor_retry_after_header() {
|
||||
await withGeoServer(
|
||||
async function cont(requests) {
|
||||
// Trigger a new request.
|
||||
await forceExpiration();
|
||||
let date = Date.now();
|
||||
let commitPromise = promiseAfterCache();
|
||||
await asyncReInit({ awaitRegionFetch: true });
|
||||
await commitPromise;
|
||||
checkRequest(requests);
|
||||
|
||||
// Check that the expiration timestamp has been updated.
|
||||
let metadata = await promiseGlobalMetadata();
|
||||
Assert.equal(typeof metadata.searchDefaultExpir, "number");
|
||||
Assert.ok(metadata.searchDefaultExpir >= date + kDayInSeconds * 1000);
|
||||
Assert.ok(
|
||||
metadata.searchDefaultExpir < date + (kDayInSeconds + 3600) * 1000
|
||||
);
|
||||
|
||||
// After another restart, a new request should not be triggered.
|
||||
await asyncReInit({ awaitRegionFetch: true });
|
||||
checkNoRequest(requests);
|
||||
},
|
||||
{ path: "lookup_unavailable" }
|
||||
);
|
||||
});
|
||||
|
|
|
@ -14,31 +14,153 @@ add_task(async function setup() {
|
|||
add_task(async function test_regular_init() {
|
||||
await withGeoServer(
|
||||
async function cont(requests) {
|
||||
Region._setRegion("us", false);
|
||||
await Services.search.init();
|
||||
await promiseAfterCache();
|
||||
let reloadObserved = false;
|
||||
let obs = (subject, topic, data) => {
|
||||
if (data == "engines-reloaded") {
|
||||
reloadObserved = true;
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
|
||||
Assert.notEqual(
|
||||
Services.search.defaultEngine.name,
|
||||
kTestEngineName,
|
||||
"Test engine shouldn't be the default"
|
||||
);
|
||||
await Promise.all([
|
||||
Services.search.init(true),
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
// If the system is under load (e.g. xpcshell-tests running in parallel),
|
||||
// then we can hit the state where init() is still running, but the cache
|
||||
// save fires early. This generates a second `write-cache-to-disk-complete`
|
||||
// which can then let us sometimes proceed early. Therefore we have this
|
||||
// additional wait to ensure that any cache saves are complete before
|
||||
// we move on.
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
checkRequest(requests);
|
||||
|
||||
// Install kTestEngineName and wait for it to reach the disk.
|
||||
await Promise.all([installTestEngine(), promiseAfterCache()]);
|
||||
|
||||
let enginesReloaded = SearchTestUtils.promiseSearchNotification(
|
||||
"engines-reloaded"
|
||||
);
|
||||
Region._setRegion("FR", true);
|
||||
await enginesReloaded;
|
||||
await promiseAfterCache();
|
||||
|
||||
Assert.equal(
|
||||
kTestEngineName,
|
||||
(await Services.search.getDefault()).name,
|
||||
"Geo defined default should be set"
|
||||
);
|
||||
checkNoRequest(requests);
|
||||
|
||||
Assert.ok(
|
||||
!reloadObserved,
|
||||
"Engines should not be reloaded during test, because region fetch succeeded"
|
||||
);
|
||||
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
},
|
||||
{ delay: 100 }
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_init_with_skip_regioncheck() {
|
||||
await withGeoServer(
|
||||
async function cont(requests) {
|
||||
let reloadObserved = false;
|
||||
let obs = (subject, topic, data) => {
|
||||
if (data == "engines-reloaded") {
|
||||
reloadObserved = true;
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
|
||||
// Break the hash.
|
||||
let metadata = await promiseGlobalMetadata();
|
||||
metadata.searchDefaultHash = "broken";
|
||||
await promiseSaveGlobalMetadata(metadata);
|
||||
|
||||
// Kick off a re-init.
|
||||
await asyncReInit();
|
||||
let otherPromises = [
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
promiseAfterCache(),
|
||||
SearchTestUtils.promiseSearchNotification(
|
||||
"engine-default",
|
||||
SEARCH_ENGINE_TOPIC
|
||||
),
|
||||
];
|
||||
|
||||
// Make sure that the original default engine is put back in place.
|
||||
Services.search.resetToOriginalDefaultEngine();
|
||||
Assert.notEqual(
|
||||
Services.search.defaultEngine.name,
|
||||
kTestEngineName,
|
||||
"Test engine shouldn't be the default anymore"
|
||||
);
|
||||
|
||||
await Promise.all(otherPromises);
|
||||
|
||||
checkRequest(requests);
|
||||
// Ensure that correct engine is being reported as the default.
|
||||
Assert.equal(
|
||||
Services.search.defaultEngine.name,
|
||||
kTestEngineName,
|
||||
"Test engine should be the default, because region fetch succeeded"
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
reloadObserved,
|
||||
"Engines should be reloaded during test, because region fetch succeeded"
|
||||
);
|
||||
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
},
|
||||
{ delay: 100 }
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_init_with_skip_regioncheck_no_engine_change() {
|
||||
await withGeoServer(
|
||||
async function cont(requests) {
|
||||
let reloadObserved = false;
|
||||
let reloadObs = (subject, topic, data) => {
|
||||
if (data == "engines-reloaded") {
|
||||
reloadObserved = true;
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(reloadObs, SEARCH_SERVICE_TOPIC);
|
||||
let engineChanged = false;
|
||||
let changeObs = (subject, topic, data) => {
|
||||
if (data == "engine-default") {
|
||||
engineChanged = true;
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(changeObs, SEARCH_ENGINE_TOPIC);
|
||||
|
||||
// Break the hash.
|
||||
let metadata = await promiseGlobalMetadata();
|
||||
metadata.searchDefaultHash = "broken";
|
||||
await promiseSaveGlobalMetadata(metadata);
|
||||
|
||||
// Kick off a re-init.
|
||||
await Promise.all([
|
||||
asyncReInit(),
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
|
||||
checkRequest(requests);
|
||||
// Ensure that correct engine is being reported as the default.
|
||||
Assert.equal(
|
||||
Services.search.defaultEngine.name,
|
||||
kTestEngineName,
|
||||
"Test engine should be the default, because region fetch succeeded"
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
reloadObserved,
|
||||
"Engines should be reloaded during test, because region fetch succeeded"
|
||||
);
|
||||
Services.obs.removeObserver(reloadObs, SEARCH_SERVICE_TOPIC);
|
||||
|
||||
Assert.ok(
|
||||
!engineChanged,
|
||||
"Engine should not have changed when a region fetch didn't change it"
|
||||
);
|
||||
Services.obs.removeObserver(changeObs, SEARCH_ENGINE_TOPIC);
|
||||
},
|
||||
{ delay: 100 }
|
||||
);
|
||||
|
|
|
@ -16,7 +16,7 @@ add_task(async function setup() {
|
|||
SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault",
|
||||
true
|
||||
);
|
||||
Region._setRegion("US", false);
|
||||
Services.prefs.setCharPref("browser.search.region", "US");
|
||||
});
|
||||
|
||||
add_task(async function test_listJSONlocale() {
|
||||
|
@ -82,7 +82,7 @@ add_task(async function test_listJSONlocaleSwitch() {
|
|||
|
||||
// Check that region overrides apply
|
||||
add_task(async function test_listJSONRegionOverride() {
|
||||
Region._setRegion("RU", false);
|
||||
Services.prefs.setCharPref("browser.search.region", "RU");
|
||||
|
||||
await asyncReInit();
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
|
||||
add_task(async function setup() {
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
|
||||
Services.prefs
|
||||
.getDefaultBranch(SearchUtils.BROWSER_SEARCH_PREF)
|
||||
.setCharPref("geoSpecificDefaults.url", "");
|
||||
});
|
||||
|
||||
add_task(async function test_location() {
|
||||
Services.prefs.setCharPref(
|
||||
"browser.region.network.url",
|
||||
'data:application/json,{"country_code": "AU"}'
|
||||
);
|
||||
await Services.search.init(true);
|
||||
equal(
|
||||
Services.prefs.getCharPref("browser.search.region"),
|
||||
"AU",
|
||||
"got the correct region."
|
||||
);
|
||||
// check we have "success" recorded in telemetry
|
||||
checkCountryResultTelemetry(TELEMETRY_RESULT_ENUM.SUCCESS);
|
||||
// simple checks for our platform-specific telemetry. We can't influence
|
||||
// what they return (as we can't influence the countryCode the platform
|
||||
// thinks we are in), but we can check the values are correct given reality.
|
||||
let probeUSMismatched, probeNonUSMismatched;
|
||||
switch (AppConstants.platform) {
|
||||
case "macosx":
|
||||
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX";
|
||||
probeNonUSMismatched =
|
||||
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX";
|
||||
break;
|
||||
case "win":
|
||||
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN";
|
||||
probeNonUSMismatched =
|
||||
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (probeUSMismatched && probeNonUSMismatched) {
|
||||
let countryCode = await Services.sysinfo.countryCode;
|
||||
print("Platform says the country-code is", countryCode);
|
||||
if (!countryCode) {
|
||||
// On treeherder for Mac the countryCode is null, so the probes won't be
|
||||
// recorded.
|
||||
// We still let the test run for Mac, as a developer would likely
|
||||
// eventually pick up on the issue.
|
||||
info("No country code set on this machine, skipping rest of test");
|
||||
return;
|
||||
}
|
||||
let expectedResult;
|
||||
let hid;
|
||||
// We know geoip said AU - if the platform thinks US then we expect
|
||||
// probeUSMismatched with true (ie, a mismatch)
|
||||
if (countryCode == "US") {
|
||||
hid = probeUSMismatched;
|
||||
expectedResult = { 0: 0, 1: 1, 2: 0 }; // boolean probe so 3 buckets, expect 1 result for |1|.
|
||||
} else {
|
||||
// We are expecting probeNonUSMismatched with false if the platform
|
||||
// says AU (not a mismatch) and true otherwise.
|
||||
hid = probeNonUSMismatched;
|
||||
expectedResult =
|
||||
countryCode == "AU" ? { 0: 1, 1: 0 } : { 0: 0, 1: 1, 2: 0 };
|
||||
}
|
||||
const histogram = Services.telemetry.getHistogramById(hid);
|
||||
let snapshot;
|
||||
await TestUtils.waitForCondition(() => {
|
||||
snapshot = histogram.snapshot();
|
||||
return snapshot.sum == 1;
|
||||
});
|
||||
|
||||
deepEqual(snapshot.values, expectedResult);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(async function setup() {
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
|
||||
});
|
||||
|
||||
add_task(async function test_location_error() {
|
||||
// We use an invalid port that parses but won't open
|
||||
let url = "http://localhost:0";
|
||||
|
||||
Services.prefs.setCharPref("browser.region.network.url", url);
|
||||
await Services.search.init();
|
||||
try {
|
||||
Services.prefs.getCharPref("browser.search.region");
|
||||
ok(false, "not expecting region to be set");
|
||||
} catch (ex) {}
|
||||
// should have an error recorded.
|
||||
checkCountryResultTelemetry(TELEMETRY_RESULT_ENUM.ERROR);
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// A console listener so we can listen for a log message from nsSearchService.
|
||||
function promiseTimezoneMessage() {
|
||||
return new Promise(resolve => {
|
||||
let listener = {
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleListener]),
|
||||
observe(msg) {
|
||||
if (
|
||||
msg.message.startsWith(
|
||||
"getIsUS() fell back to a timezone check with the result="
|
||||
)
|
||||
) {
|
||||
Services.console.unregisterListener(listener);
|
||||
resolve(msg);
|
||||
}
|
||||
},
|
||||
};
|
||||
Services.console.registerListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
Services.prefs.setBoolPref("browser.region.log", true);
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
|
||||
});
|
||||
|
||||
add_task(async function test_location_malformed_json() {
|
||||
// Here we have malformed JSON
|
||||
Services.prefs.setCharPref(
|
||||
"browser.region.network.url",
|
||||
'data:application/json,{"country_code"'
|
||||
);
|
||||
await Services.search.init();
|
||||
ok(
|
||||
!Services.prefs.prefHasUserValue("browser.search.region"),
|
||||
"should be no region pref"
|
||||
);
|
||||
// fetch the engines - this should not persist any prefs.
|
||||
await Services.search.getEngines();
|
||||
ok(
|
||||
!Services.prefs.prefHasUserValue("browser.search.region"),
|
||||
"should be no region pref"
|
||||
);
|
||||
// should have recorded SUCCESS_WITHOUT_DATA
|
||||
checkCountryResultTelemetry(TELEMETRY_RESULT_ENUM.SUCCESS_WITHOUT_DATA);
|
||||
});
|
|
@ -29,10 +29,10 @@ add_task(async function basic_multilocale_test() {
|
|||
let initPromise = new Promise(resolve => (resolver = resolve));
|
||||
useCustomGeoServer("FR", initPromise);
|
||||
|
||||
await Services.search.init();
|
||||
await Services.search.init(false);
|
||||
await Services.search.getDefaultEngines();
|
||||
resolver();
|
||||
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
|
||||
await SearchTestUtils.promiseSearchNotification("ensure-known-region-done");
|
||||
|
||||
let engines = await Services.search.getDefaultEngines();
|
||||
|
||||
|
|
|
@ -8,27 +8,44 @@ const SEARCH_SERVICE_TOPIC = "browser-search-service";
|
|||
add_task(async function setup() {
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
await useTestEngines("data", "geolookup-extensions");
|
||||
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
|
||||
});
|
||||
|
||||
add_task(async function test_maybereloadengine_update() {
|
||||
await Services.search.init();
|
||||
let reloadObserved = false;
|
||||
let obs = (subject, topic, data) => {
|
||||
if (data == "engines-reloaded") {
|
||||
reloadObserved = true;
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
|
||||
let defaultEngine = await Services.search.getDefault();
|
||||
Assert.equal(
|
||||
defaultEngine._shortName,
|
||||
"multilocale-an",
|
||||
"Should have update to the new engine with the same name"
|
||||
);
|
||||
let initPromise = Services.search.init(false);
|
||||
|
||||
let enginesReloaded = SearchTestUtils.promiseSearchNotification(
|
||||
"engines-reloaded"
|
||||
);
|
||||
Region._setRegion("FR", true);
|
||||
await enginesReloaded;
|
||||
async function cont(requests) {
|
||||
await Promise.all([
|
||||
initPromise,
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
|
||||
Assert.equal(
|
||||
defaultEngine._shortName,
|
||||
"multilocale-af",
|
||||
"Should have update to the new engine with the same name"
|
||||
);
|
||||
Assert.ok(
|
||||
reloadObserved,
|
||||
"Engines should be reloaded during test, because region fetch succeeded"
|
||||
);
|
||||
|
||||
let defaultEngine = await Services.search.getDefault();
|
||||
Assert.equal(
|
||||
defaultEngine._shortName,
|
||||
"multilocale-af",
|
||||
"Should have update to the new engine with the same name"
|
||||
);
|
||||
|
||||
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
}
|
||||
|
||||
await withGeoServer(cont, {
|
||||
geoLookupData: { country_code: "FR" },
|
||||
preGeolookupPromise: initPromise,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,31 +17,57 @@ add_task(async function setup() {
|
|||
|
||||
installDistributionEngine();
|
||||
|
||||
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
});
|
||||
|
||||
add_task(async function test_maybereloadengine_update_distro() {
|
||||
await Services.search.init();
|
||||
let reloadObserved = false;
|
||||
let obs = (subject, topic, data) => {
|
||||
if (data == "engines-reloaded") {
|
||||
reloadObserved = true;
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
|
||||
Region._setRegion("FR", true);
|
||||
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
|
||||
let initPromise = Services.search.init(false);
|
||||
|
||||
let defaultEngine = await Services.search.getDefault();
|
||||
Assert.equal(
|
||||
defaultEngine._shortName,
|
||||
"basic",
|
||||
"Should have kept the same name"
|
||||
);
|
||||
Assert.equal(
|
||||
defaultEngine._loadPath,
|
||||
"[distribution]/searchplugins/common/basic.xml",
|
||||
"Should have kept the distribution engine"
|
||||
);
|
||||
Assert.equal(
|
||||
defaultEngine
|
||||
._getURLOfType("text/html")
|
||||
.getSubmission("", defaultEngine, "searchbar").uri.spec,
|
||||
"http://searchtest.local/?search=",
|
||||
"Should have kept the same submission URL"
|
||||
);
|
||||
async function cont(requests) {
|
||||
await Promise.all([
|
||||
initPromise,
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
|
||||
Assert.ok(
|
||||
reloadObserved,
|
||||
"Engines should be reloaded during test, because region fetch succeeded"
|
||||
);
|
||||
|
||||
let defaultEngine = await Services.search.getDefault();
|
||||
Assert.equal(
|
||||
defaultEngine._shortName,
|
||||
"basic",
|
||||
"Should have kept the same name"
|
||||
);
|
||||
Assert.equal(
|
||||
defaultEngine._loadPath,
|
||||
"[distribution]/searchplugins/common/basic.xml",
|
||||
"Should have kept the distribution engine"
|
||||
);
|
||||
Assert.equal(
|
||||
defaultEngine
|
||||
._getURLOfType("text/html")
|
||||
.getSubmission("", defaultEngine, "searchbar").uri.spec,
|
||||
"http://searchtest.local/?search=",
|
||||
"Should have kept the same submission URL"
|
||||
);
|
||||
|
||||
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
|
||||
}
|
||||
|
||||
await withGeoServer(cont, {
|
||||
geoLookupData: { country_code: "FR" },
|
||||
preGeolookupPromise: initPromise,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
"use strict";
|
||||
|
||||
add_task(async function setup() {
|
||||
Region._setRegion("an", false);
|
||||
Services.prefs.setCharPref("browser.search.region", "an");
|
||||
|
||||
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
|
||||
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
await useTestEngines("test-extensions");
|
||||
});
|
||||
|
@ -48,7 +51,7 @@ add_task(async function test_changeRegion() {
|
|||
let reInitPromise = SearchTestUtils.promiseSearchNotification(
|
||||
"reinit-complete"
|
||||
);
|
||||
Region._setRegion("tr", false);
|
||||
Services.prefs.setCharPref("browser.search.region", "tr");
|
||||
Services.search.reInit();
|
||||
await reInitPromise;
|
||||
|
||||
|
|
|
@ -156,12 +156,13 @@ add_task(async function setup() {
|
|||
});
|
||||
|
||||
add_task(async function test_regular_init() {
|
||||
let reloadObserved = listenFor(SEARCH_SERVICE_TOPIC, "engines-reloaded");
|
||||
let geoUrl = `data:application/json,{"country_code": "FR"}`;
|
||||
Services.prefs.setCharPref("browser.region.network.url", geoUrl);
|
||||
|
||||
await Promise.all([
|
||||
Services.search.init(),
|
||||
SearchTestUtils.promiseSearchNotification("engines-reloaded"),
|
||||
Services.search.init(true),
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
promiseAfterCache(),
|
||||
]);
|
||||
|
||||
|
@ -170,6 +171,11 @@ add_task(async function test_regular_init() {
|
|||
FR_DEFAULT,
|
||||
"Geo defined default should be set"
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
!reloadObserved(),
|
||||
"Engines don't reload with immediate region fetch"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_init_with_slow_region_lookup() {
|
||||
|
@ -178,6 +184,7 @@ add_task(async function test_init_with_slow_region_lookup() {
|
|||
|
||||
// Ensure the region lookup completes after init so the
|
||||
// engines are reloaded
|
||||
Services.prefs.setCharPref("browser.search.region", "");
|
||||
let srv = useHttpServer();
|
||||
srv.registerPathHandler("/fetch_region", async (req, res) => {
|
||||
res.processAsync();
|
||||
|
@ -192,14 +199,12 @@ add_task(async function test_init_with_slow_region_lookup() {
|
|||
`http://localhost:${srv.identity.primaryPort}/fetch_region`
|
||||
);
|
||||
|
||||
Region._setRegion("", false);
|
||||
Region.init();
|
||||
|
||||
// Kick off a re-init.
|
||||
initPromise = asyncReInit();
|
||||
await initPromise;
|
||||
|
||||
let otherPromises = [
|
||||
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
|
||||
promiseAfterCache(),
|
||||
SearchTestUtils.promiseSearchNotification(
|
||||
"engine-default",
|
||||
|
|
|
@ -15,7 +15,7 @@ SearchTestUtils.initXPCShellAddonManager(this);
|
|||
async function restart() {
|
||||
Services.search.reset();
|
||||
await promiseRestartManager();
|
||||
await Services.search.init();
|
||||
await Services.search.init(false);
|
||||
}
|
||||
|
||||
async function getEngineNames() {
|
||||
|
@ -61,7 +61,7 @@ add_task(async function basic_install_test() {
|
|||
|
||||
add_task(async function basic_multilocale_test() {
|
||||
await forceExpiration();
|
||||
Region._setRegion("an", false);
|
||||
Services.prefs.setCharPref("browser.search.region", "an");
|
||||
|
||||
await withGeoServer(
|
||||
async function cont(requests) {
|
||||
|
@ -78,7 +78,7 @@ add_task(async function basic_multilocale_test() {
|
|||
|
||||
add_task(async function complex_multilocale_test() {
|
||||
await forceExpiration();
|
||||
Region._setRegion("af", false);
|
||||
Services.prefs.setCharPref("browser.search.region", "af");
|
||||
|
||||
await withGeoServer(
|
||||
async function cont(requests) {
|
||||
|
@ -95,7 +95,7 @@ add_task(async function complex_multilocale_test() {
|
|||
});
|
||||
add_task(async function test_manifest_selection() {
|
||||
await forceExpiration();
|
||||
Region._setRegion("an", false);
|
||||
Services.prefs.setCharPref("browser.search.region", "an");
|
||||
Services.locale.availableLocales = ["af"];
|
||||
Services.locale.requestedLocales = ["af"];
|
||||
|
||||
|
|
|
@ -43,6 +43,9 @@ support-files =
|
|||
data1/engines.json
|
||||
[test_list_json_searchdefault.js]
|
||||
[test_list_json_searchorder.js]
|
||||
[test_location_error.js]
|
||||
[test_location_malformed_json.js]
|
||||
[test_location.js]
|
||||
[test_migrateWebExtensionEngine.js]
|
||||
[test_multipleIcons.js]
|
||||
[test_nocache.js]
|
||||
|
|
|
@ -70,6 +70,7 @@ support-files =
|
|||
[test_engine_selector_application_distribution.js]
|
||||
[test_engine_selector_application_name.js]
|
||||
[test_reload_engines.js]
|
||||
[test_location_timeout.js]
|
||||
[test_missing_engine.js]
|
||||
[test_sort_orders-no-hints.js]
|
||||
[test_webextensions_builtin_upgrade.js]
|
||||
|
|
|
@ -11,7 +11,6 @@ const { XPCOMUtils } = ChromeUtils.import(
|
|||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
LocationHelper: "resource://gre/modules/LocationHelper.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
setTimeout: "resource://gre/modules/Timer.jsm",
|
||||
|
@ -40,19 +39,14 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
|||
false
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
"geoSpecificDefaults",
|
||||
"browser.search.geoSpecificDefaults",
|
||||
false
|
||||
);
|
||||
|
||||
const log = console.createInstance({
|
||||
prefix: "Region.jsm",
|
||||
maxLogLevel: loggingEnabled ? "All" : "Warn",
|
||||
});
|
||||
|
||||
const REGION_PREF = "browser.search.region";
|
||||
function timeout(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* This module keeps track of the users current region (country).
|
||||
|
@ -65,172 +59,22 @@ class RegionDetector {
|
|||
// Store the AbortController for the geolocation requests so we
|
||||
// can abort the request on timeout.
|
||||
fetchController = null;
|
||||
// Topic for Observer events fired by Region.jsm.
|
||||
REGION_TOPIC = "browser-region";
|
||||
// Verb for event fired when we update the region.
|
||||
REGION_UPDATED = "region-updated";
|
||||
// Values for telemetry.
|
||||
TELEMETRY = {
|
||||
SUCCESS: 0,
|
||||
NO_RESULT: 1,
|
||||
TIMEOUT: 2,
|
||||
ERROR: 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Read currently stored region data and if needed trigger background
|
||||
* region detection.
|
||||
*/
|
||||
init() {
|
||||
let region = Services.prefs.getCharPref(REGION_PREF, null);
|
||||
if (!region) {
|
||||
Services.tm.idleDispatchToMainThread(this._fetchRegion.bind(this));
|
||||
}
|
||||
this._home = region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the region we currently consider the users home.
|
||||
* Fetch the region that we consider to be the users home region.
|
||||
*
|
||||
* @returns {string}
|
||||
* The users current home region.
|
||||
* @returns {object}
|
||||
* JSON with country_code field defining users current region.
|
||||
*/
|
||||
get home() {
|
||||
return this._home;
|
||||
async getHomeRegion() {
|
||||
return Promise.race([this._getRegion(), this._timeout()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the users current region.
|
||||
*
|
||||
* @returns {string}
|
||||
* The country_code defining users current region.
|
||||
*/
|
||||
async _fetchRegion() {
|
||||
if (!geoSpecificDefaults) {
|
||||
return null;
|
||||
}
|
||||
let startTime = Date.now();
|
||||
let telemetryResult = this.TELEMETRY.SUCCESS;
|
||||
let result = null;
|
||||
|
||||
try {
|
||||
result = await Promise.race([
|
||||
this._getRegion(),
|
||||
timeout(regionFetchTimeout),
|
||||
]);
|
||||
} catch (err) {
|
||||
telemetryResult = this.TELEMETRY[err.message] || this.TELEMETRY.ERROR;
|
||||
log.error("Failed to fetch region", err);
|
||||
}
|
||||
|
||||
let took = Date.now() - startTime;
|
||||
if (result) {
|
||||
await this._storeRegion(result);
|
||||
}
|
||||
Services.telemetry
|
||||
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS")
|
||||
.add(took);
|
||||
|
||||
Services.telemetry
|
||||
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_RESULT")
|
||||
.add(telemetryResult);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate then store the region and report telemetry.
|
||||
*
|
||||
* @param region
|
||||
* The region to store.
|
||||
*/
|
||||
async _storeRegion(region) {
|
||||
let prefix = "SEARCH_SERVICE";
|
||||
let isTimezoneUS = isUSTimezone();
|
||||
// If it's a US region, but not a US timezone, we don't store
|
||||
// the value. This works because no region defaults to
|
||||
// ZZ (unknown) in nsURLFormatter
|
||||
if (region != "US" || isTimezoneUS) {
|
||||
log.info("Saving home region:", region);
|
||||
this._setRegion(region, true);
|
||||
}
|
||||
|
||||
// and telemetry...
|
||||
if (region == "US" && !isTimezoneUS) {
|
||||
log.info("storeRegion mismatch - US Region, non-US timezone");
|
||||
Services.telemetry
|
||||
.getHistogramById(`${prefix}_US_COUNTRY_MISMATCHED_TIMEZONE`)
|
||||
.add(1);
|
||||
}
|
||||
if (region != "US" && isTimezoneUS) {
|
||||
log.info("storeRegion mismatch - non-US Region, US timezone");
|
||||
Services.telemetry
|
||||
.getHistogramById(`${prefix}_US_TIMEZONE_MISMATCHED_COUNTRY`)
|
||||
.add(1);
|
||||
}
|
||||
// telemetry to compare our geoip response with
|
||||
// platform-specific country data.
|
||||
// On Mac and Windows, we can get a country code via sysinfo
|
||||
let platformCC = await Services.sysinfo.countryCode;
|
||||
if (platformCC) {
|
||||
let probeUSMismatched, probeNonUSMismatched;
|
||||
switch (AppConstants.platform) {
|
||||
case "macosx":
|
||||
probeUSMismatched = `${prefix}_US_COUNTRY_MISMATCHED_PLATFORM_OSX`;
|
||||
probeNonUSMismatched = `${prefix}_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX`;
|
||||
break;
|
||||
case "win":
|
||||
probeUSMismatched = `${prefix}_US_COUNTRY_MISMATCHED_PLATFORM_WIN`;
|
||||
probeNonUSMismatched = `${prefix}_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN`;
|
||||
break;
|
||||
default:
|
||||
log.error(
|
||||
"Platform " +
|
||||
Services.appinfo.OS +
|
||||
" has system country code but no search service telemetry probes"
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (probeUSMismatched && probeNonUSMismatched) {
|
||||
if (region == "US" || platformCC == "US") {
|
||||
// one of the 2 said US, so record if they are the same.
|
||||
Services.telemetry
|
||||
.getHistogramById(probeUSMismatched)
|
||||
.add(region != platformCC);
|
||||
} else {
|
||||
// non-US - record if they are the same
|
||||
Services.telemetry
|
||||
.getHistogramById(probeNonUSMismatched)
|
||||
.add(region != platformCC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the updated region and notify observers.
|
||||
*
|
||||
* @param region
|
||||
* The region to store.
|
||||
*/
|
||||
_setRegion(region = "", notify = false) {
|
||||
this._home = region;
|
||||
Services.prefs.setCharPref("browser.search.region", region);
|
||||
if (notify) {
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
this.REGION_TOPIC,
|
||||
this.REGION_UPDATED,
|
||||
region
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the request to fetch the region from the configured service.
|
||||
* Implementation of the above region fetching.
|
||||
*/
|
||||
async _getRegion() {
|
||||
log.info("_getRegion called");
|
||||
log.info("getRegion called");
|
||||
this.fetchController = new AbortController();
|
||||
let fetchOpts = {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
@ -252,11 +96,11 @@ class RegionDetector {
|
|||
try {
|
||||
let res = await req.json();
|
||||
this.fetchController = null;
|
||||
log.info("_getRegion returning ", res.country_code);
|
||||
return res.country_code;
|
||||
log.info("getRegion returning " + res.country_code);
|
||||
return { country_code: res.country_code };
|
||||
} catch (err) {
|
||||
log.error("Error fetching region", err);
|
||||
throw new Error("NO_RESULT");
|
||||
throw new Error("region-fetch-no-result");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +114,7 @@ class RegionDetector {
|
|||
if (this.fetchController) {
|
||||
this.fetchController.abort();
|
||||
}
|
||||
throw new Error("TIMEOUT");
|
||||
throw new Error("region-fetch-timeout");
|
||||
}
|
||||
|
||||
async _fetchWifiData() {
|
||||
|
@ -305,26 +149,3 @@ class RegionDetector {
|
|||
}
|
||||
|
||||
let Region = new RegionDetector();
|
||||
Region.init();
|
||||
|
||||
function timeout(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// A method that tries to determine if this user is in a US geography.
|
||||
function isUSTimezone() {
|
||||
// Timezone assumptions! We assume that if the system clock's timezone is
|
||||
// between Newfoundland and Hawaii, that the user is in North America.
|
||||
|
||||
// This includes all of South America as well, but we have relatively few
|
||||
// en-US users there, so that's OK.
|
||||
|
||||
// 150 minutes = 2.5 hours (UTC-2.5), which is
|
||||
// Newfoundland Daylight Time (http://www.timeanddate.com/time/zones/ndt)
|
||||
|
||||
// 600 minutes = 10 hours (UTC-10), which is
|
||||
// Hawaii-Aleutian Standard Time (http://www.timeanddate.com/time/zones/hast)
|
||||
|
||||
let UTCOffset = new Date().getTimezoneOffset();
|
||||
return UTCOffset >= 150 && UTCOffset <= 600;
|
||||
}
|
||||
|
|
|
@ -1,90 +1,16 @@
|
|||
"use strict";
|
||||
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
|
||||
const { Region } = ChromeUtils.import("resource://gre/modules/Region.jsm");
|
||||
const { TestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/TestUtils.jsm"
|
||||
);
|
||||
|
||||
const REGION_PREF = "browser.region.network.url";
|
||||
|
||||
const RESPONSE_DELAY = 500;
|
||||
const RESPONSE_TIMEOUT = 100;
|
||||
|
||||
const histogram = Services.telemetry.getHistogramById(
|
||||
"SEARCH_SERVICE_COUNTRY_FETCH_RESULT"
|
||||
);
|
||||
|
||||
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
|
||||
|
||||
add_task(async function test_basic() {
|
||||
let srv = useHttpServer();
|
||||
srv.registerPathHandler("/geo", (req, res) => {
|
||||
res.setStatusLine("1.1", 200, "OK");
|
||||
send(res, { country_code: "UK" });
|
||||
});
|
||||
|
||||
await Region._fetchRegion();
|
||||
Assert.ok(true, "Region fetch should succeed");
|
||||
Assert.equal(Region.home, "UK", "Region fetch should return correct result");
|
||||
|
||||
await new Promise(r => srv.stop(r));
|
||||
});
|
||||
|
||||
add_task(async function test_invalid_url() {
|
||||
histogram.clear();
|
||||
Services.prefs.setCharPref(REGION_PREF, "http://localhost:0");
|
||||
let result = await Region._fetchRegion();
|
||||
Assert.ok(!result, "Should return no result");
|
||||
checkTelemetry(Region.TELEMETRY.ERROR);
|
||||
});
|
||||
|
||||
add_task(async function test_invalid_json() {
|
||||
histogram.clear();
|
||||
Services.prefs.setCharPref(
|
||||
REGION_PREF,
|
||||
'data:application/json,{"country_code"'
|
||||
);
|
||||
let result = await Region._fetchRegion();
|
||||
Assert.ok(!result, "Should return no result");
|
||||
checkTelemetry(Region.TELEMETRY.NO_RESULT);
|
||||
});
|
||||
|
||||
add_task(async function test_mismatched_probe() {
|
||||
let probeDetails = await getExpectedHistogramDetails();
|
||||
let probeHistogram;
|
||||
if (probeDetails) {
|
||||
probeHistogram = Services.telemetry.getHistogramById(probeDetails.probeId);
|
||||
probeHistogram.clear();
|
||||
}
|
||||
histogram.clear();
|
||||
|
||||
Services.prefs.setCharPref(
|
||||
REGION_PREF,
|
||||
'data:application/json,{"country_code": "AU"}'
|
||||
);
|
||||
await Region._fetchRegion();
|
||||
Assert.equal(Region.home, "AU", "Should have correct region");
|
||||
checkTelemetry(Region.TELEMETRY.SUCCESS);
|
||||
|
||||
// We dont store probes for linux and on treeherder +
|
||||
// Mac there is no plaform countryCode so in these cases
|
||||
// skip the rest of the checks.
|
||||
if (!probeDetails) {
|
||||
return;
|
||||
}
|
||||
let snapshot = probeHistogram.snapshot();
|
||||
deepEqual(snapshot.values, probeDetails.expectedResult);
|
||||
});
|
||||
|
||||
function useHttpServer() {
|
||||
let server = new HttpServer();
|
||||
server.start(-1);
|
||||
Services.prefs.setCharPref(
|
||||
REGION_PREF,
|
||||
"browser.region.network.url",
|
||||
`http://localhost:${server.identity.primaryPort}/geo`
|
||||
);
|
||||
return server;
|
||||
|
@ -96,62 +22,44 @@ function send(res, json) {
|
|||
res.write(JSON.stringify(json));
|
||||
}
|
||||
|
||||
async function checkTelemetry(aExpectedValue) {
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return histogram.snapshot().sum == 1;
|
||||
add_task(async function test_basic() {
|
||||
let srv = useHttpServer();
|
||||
|
||||
srv.registerPathHandler("/geo", (req, res) => {
|
||||
res.setStatusLine("1.1", 200, "OK");
|
||||
send(res, { country_code: "US" });
|
||||
});
|
||||
let snapshot = histogram.snapshot();
|
||||
Assert.equal(snapshot.values[aExpectedValue], 1);
|
||||
}
|
||||
|
||||
// Define some checks for our platform-specific telemetry.
|
||||
// We can't influence what they return (as we can't
|
||||
// influence the countryCode the platform thinks we
|
||||
// are in), but we can check the values are
|
||||
// correct given reality.
|
||||
async function getExpectedHistogramDetails() {
|
||||
let probeUSMismatched, probeNonUSMismatched;
|
||||
switch (AppConstants.platform) {
|
||||
case "macosx":
|
||||
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX";
|
||||
probeNonUSMismatched =
|
||||
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX";
|
||||
break;
|
||||
case "win":
|
||||
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN";
|
||||
probeNonUSMismatched =
|
||||
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
let res = await Region.getHomeRegion();
|
||||
Assert.ok(true, "Region fetch should succeed");
|
||||
Assert.equal(
|
||||
res.country_code,
|
||||
"US",
|
||||
"Region fetch should return correct result"
|
||||
);
|
||||
|
||||
await new Promise(r => srv.stop(r));
|
||||
});
|
||||
|
||||
add_task(async function test_timeout() {
|
||||
let srv = useHttpServer();
|
||||
|
||||
srv.registerPathHandler("/geo", (req, res) => {
|
||||
res.processAsync();
|
||||
do_timeout(RESPONSE_DELAY, () => {
|
||||
send(res, { country_code: "US" });
|
||||
res.finish();
|
||||
});
|
||||
});
|
||||
|
||||
Services.prefs.setIntPref("browser.region.timeout", RESPONSE_TIMEOUT);
|
||||
|
||||
try {
|
||||
await Region.getHomeRegion();
|
||||
Assert.ok(false, "Region fetch should have timed out");
|
||||
} catch (e) {
|
||||
Assert.equal(e.message, "region-fetch-timeout", "Region fetch timed out");
|
||||
}
|
||||
|
||||
if (probeUSMismatched && probeNonUSMismatched) {
|
||||
let countryCode = await Services.sysinfo.countryCode;
|
||||
print("Platform says the country-code is", countryCode);
|
||||
if (!countryCode) {
|
||||
// On treeherder for Mac the countryCode is null, so the probes won't be
|
||||
// recorded.
|
||||
// We still let the test run for Mac, as a developer would likely
|
||||
// eventually pick up on the issue.
|
||||
info("No country code set on this machine, skipping rest of test");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (countryCode == "US") {
|
||||
// boolean probe so 3 buckets, expect 1 result for |1|.
|
||||
return {
|
||||
probeId: probeUSMismatched,
|
||||
expectedResult: { 0: 0, 1: 1, 2: 0 },
|
||||
};
|
||||
}
|
||||
// We are expecting probeNonUSMismatched with false if the platform
|
||||
// says AU (not a mismatch) and true otherwise.
|
||||
return {
|
||||
probeId: probeNonUSMismatched,
|
||||
expectedResult:
|
||||
countryCode == "AU" ? { 0: 1, 1: 0 } : { 0: 0, 1: 1, 2: 0 },
|
||||
};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
await new Promise(r => srv.stop(r));
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче