Bug 1627555 - Update region if in new location for a length of time. r=Standard8

Differential Revision: https://phabricator.services.mozilla.com/D79272
This commit is contained in:
Dale Harvey 2020-06-25 22:20:21 +00:00
parent 7c995807da
commit ad16313f52
14 changed files with 150 additions and 40 deletions

View File

@ -52,14 +52,14 @@ add_task(async function() {
);
// Set language back to en-US
Services.prefs.setCharPref("intl.accept_languages", "en-US");
Region._setRegion("US", false);
Region._setHomeRegion("US", false);
await reloadTab(tab);
await checkProxyCardVisibility(tab, false);
info(
"Check that secure proxy card is hidden if user's location is not in the US."
);
Region._setRegion("CA", false);
Region._setHomeRegion("CA", false);
await reloadTab(tab);
await checkProxyCardVisibility(tab, true);
@ -67,7 +67,7 @@ add_task(async function() {
"Check that secure proxy card is hidden if the extension is already installed."
);
// Make sure we set the region back to "US"
Region._setRegion("US", false);
Region._setHomeRegion("US", false);
const id = "secure-proxy@mozilla.com";
const extension = ExtensionTestUtils.loadExtension({
manifest: {

View File

@ -29,7 +29,7 @@ add_task(async function test_cancelEditAddressDialogWithESC() {
});
add_task(async function test_defaultCountry() {
Region._setRegion("CA", false);
Region._setHomeRegion("CA", false);
await testDialog(EDIT_ADDRESS_DIALOG_URL, win => {
let doc = win.document;
is(
@ -39,7 +39,7 @@ add_task(async function test_defaultCountry() {
);
doc.querySelector("#cancel").click();
});
Region._setRegion("DE", false);
Region._setHomeRegion("DE", false);
await testDialog(EDIT_ADDRESS_DIALOG_URL, win => {
let doc = win.document;
is(
@ -50,13 +50,13 @@ add_task(async function test_defaultCountry() {
doc.querySelector("#cancel").click();
});
// Test unsupported country
Region._setRegion("XX", false);
Region._setHomeRegion("XX", false);
await testDialog(EDIT_ADDRESS_DIALOG_URL, win => {
let doc = win.document;
is(doc.querySelector("#country").value, "", "Default country set to empty");
doc.querySelector("#cancel").click();
});
Region._setRegion("US", false);
Region._setHomeRegion("US", false);
});
add_task(async function test_saveAddress() {
@ -864,7 +864,7 @@ add_task(async function test_hiddenFieldRemovedWhenCountryChanged() {
});
add_task(async function test_countrySpecificFieldsGetRequiredness() {
Region._setRegion("RO", false);
Region._setHomeRegion("RO", false);
await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => {
let doc = win.document;
is(

View File

@ -3889,6 +3889,10 @@ pref("browser.region.network.scan", false);
// Timeout for whole region request.
pref("browser.region.timeout", 5000);
#ifdef EARLY_BETA_OR_EARLIER
pref("browser.region.update.enabled", true);
#endif
// Enable/Disable the device storage API for content
pref("device.storage.enabled", false);

View File

@ -215,7 +215,7 @@ class SearchConfigTest {
* The two-letter locale code.
*/
async _reinit(region, locale) {
Region._setRegion(region?.toUpperCase(), true);
Region._setHomeRegion(region?.toUpperCase(), true);
if (region) {
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
}

View File

@ -50,7 +50,7 @@ add_task(async function should_get_geo_defaults_only_once() {
await withGeoServer(async function cont(requests) {
// (Re)initializing the search service should trigger a request,
// and set the default engine based on it.
Region._setRegion("FR", false);
Region._setHomeRegion("FR", false);
await Promise.all([asyncReInitReloaded(), promiseAfterCache()]);
Assert.equal((await Services.search.getDefault()).name, kTestEngineName);

View File

@ -14,7 +14,7 @@ add_task(async function setup() {
add_task(async function test_regular_init() {
await withGeoServer(
async function cont(requests) {
Region._setRegion("us", false);
Region._setHomeRegion("us", false);
await Services.search.init();
await promiseAfterCache();
@ -30,7 +30,7 @@ add_task(async function test_regular_init() {
let enginesReloaded = SearchTestUtils.promiseSearchNotification(
"engines-reloaded"
);
Region._setRegion("FR", true);
Region._setHomeRegion("FR");
await promiseAfterCache();
await enginesReloaded;

View File

@ -16,7 +16,7 @@ add_task(async function setup() {
SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault",
true
);
Region._setRegion("US", false);
Region._setHomeRegion("US", false);
});
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);
Region._setHomeRegion("RU", false);
await asyncReInit();

View File

@ -23,7 +23,7 @@ add_task(async function test_maybereloadengine_update() {
let enginesReloaded = SearchTestUtils.promiseSearchNotification(
"engines-reloaded"
);
Region._setRegion("FR", true);
Region._setHomeRegion("FR", true);
await enginesReloaded;
Assert.equal(

View File

@ -23,7 +23,7 @@ add_task(async function setup() {
add_task(async function test_maybereloadengine_update_distro() {
await Services.search.init();
Region._setRegion("FR", true);
Region._setHomeRegion("FR", true);
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
let defaultEngine = await Services.search.getDefault();

View File

@ -8,7 +8,7 @@
"use strict";
add_task(async function setup() {
Region._setRegion("an", false);
Region._setHomeRegion("an", false);
await AddonTestUtils.promiseStartupManager();
await useTestEngines("test-extensions");
});
@ -48,7 +48,7 @@ add_task(async function test_changeRegion() {
let reInitPromise = SearchTestUtils.promiseSearchNotification(
"reinit-complete"
);
Region._setRegion("tr", false);
Region._setHomeRegion("tr", false);
Services.search.reInit();
await reInitPromise;

View File

@ -159,7 +159,7 @@ add_task(async function test_init_with_slow_region_lookup() {
`http://localhost:${srv.identity.primaryPort}/fetch_region`
);
Region._setRegion("", false);
Region._setHomeRegion("", false);
Region.init();
// Kick off a re-init.

View File

@ -65,7 +65,7 @@ add_task(async function basic_install_test() {
add_task(async function basic_multilocale_test() {
await forceExpiration();
Region._setRegion("an", false);
Region._setHomeRegion("an");
await withGeoServer(
async function cont(requests) {
@ -82,7 +82,7 @@ add_task(async function basic_multilocale_test() {
add_task(async function complex_multilocale_test() {
await forceExpiration();
Region._setRegion("af", false);
Region._setHomeRegion("af", false);
await withGeoServer(
async function cont(requests) {
@ -99,7 +99,7 @@ add_task(async function complex_multilocale_test() {
});
add_task(async function test_manifest_selection() {
await forceExpiration();
Region._setRegion("an", false);
Region._setHomeRegion("an", false);
Services.locale.availableLocales = ["af"];
Services.locale.requestedLocales = ["af"];

View File

@ -44,6 +44,13 @@ XPCOMUtils.defineLazyPreferenceGetter(
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"cacheBustEnabled",
"browser.region.update.enabled",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"localGeocodingEnabled",
@ -59,16 +66,28 @@ const log = console.createInstance({
const REGION_PREF = "browser.search.region";
const COLLECTION_ID = "regions";
// Prefix for all the region updating related preferences.
const UPDATE_PREFIX = "browser.region.update";
// The amount of time (in seconds) we need to be in a new
// location before we update the home region.
// Currently set to 2 weeks.
const UPDATE_INTERVAL = 60 * 60 * 24 * 14;
/**
* This module keeps track of the users current region (country).
* so the SearchService and other consumers can apply region
* specific customisations.
*/
class RegionDetector {
// The users home location.
_home = null;
// The most recent location the user was detected.
_current = null;
// The RemoteSettings client used to sync region files.
_rsClient = null;
// Keep track of the wifi data across listener events.
wifiDataPromise = null;
_wifiDataPromise = null;
// Topic for Observer events fired by Region.jsm.
REGION_TOPIC = "browser-region";
// Verb for event fired when we update the region.
@ -86,8 +105,8 @@ class RegionDetector {
* region detection.
*/
init() {
let region = Services.prefs.getCharPref(REGION_PREF, null);
if (!region) {
this._home = Services.prefs.getCharPref(REGION_PREF, null);
if (cacheBustEnabled || !this._home) {
Services.tm.idleDispatchToMainThread(this._fetchRegion.bind(this));
}
if (localGeocodingEnabled) {
@ -95,7 +114,6 @@ class RegionDetector {
this._setupRemoteSettings.bind(this)
);
}
this._home = region;
}
/**
@ -108,6 +126,16 @@ class RegionDetector {
return this._home;
}
/**
* Get the last region we detected the user to be in.
*
* @returns {string}
* The users current region.
*/
get current() {
return this._current;
}
/**
* Fetch the users current region.
*
@ -154,8 +182,7 @@ class RegionDetector {
// 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);
this._setCurrentRegion(region, true);
}
// and telemetry...
@ -211,12 +238,64 @@ class RegionDetector {
}
/**
* Save the updated region and notify observers.
* Save the update current region and check if the home region
* also needs an update.
*
* @param region
* @param {string} region
* The region to store.
*/
_setRegion(region = "", notify = false) {
_setCurrentRegion(region = "") {
log.info("Setting current region:", region);
this._current = region;
let prefs = Services.prefs;
// Interval is in seconds.
let interval = prefs.getIntPref(
`${UPDATE_PREFIX}.interval`,
UPDATE_INTERVAL
);
let seenRegion = prefs.getCharPref(`${UPDATE_PREFIX}.region`, null);
let firstSeen = prefs.getIntPref(`${UPDATE_PREFIX}.first-seen`, 0);
// If we don't have a value for .home we can set it immediately.
if (!this._home) {
this._setHomeRegion(region);
} else if (region != this._home && region != seenRegion) {
// If we are in a different region than what is currently
// considered home, then keep track of when we first
// seen the new location.
prefs.setCharPref(`${UPDATE_PREFIX}.region`, region);
prefs.setIntPref(
`${UPDATE_PREFIX}.first-seen`,
Math.round(Date.now() / 1000)
);
} else if (region != this._home && region == seenRegion) {
// If we have been in the new region for longer than
// a specified time period, then set that as the new home.
if (Math.round(Date.now() / 1000) >= firstSeen + interval) {
this._setHomeRegion(region);
}
} else {
// If we are at home again, stop tracking the seen region.
prefs.clearUserPref(`${UPDATE_PREFIX}.region`);
prefs.clearUserPref(`${UPDATE_PREFIX}.first-seen`);
}
}
/**
* Save the updated home region and notify observers.
*
* @param {string} region
* The region to store.
* @param {boolean} [notify]
* Tests can disable the notification for convenience as it
* may trigger an engines reload.
*/
_setHomeRegion(region, notify = true) {
if (region == this._home) {
return;
}
log.info("Updating home region:", region);
this._home = region;
Services.prefs.setCharPref("browser.search.region", region);
if (notify) {
@ -567,13 +646,13 @@ class RegionDetector {
this.wifiService.startWatching(this);
return new Promise(resolve => {
this.wifiDataPromise = resolve;
this._wifiDataPromise = resolve;
});
}
onChange(accessPoints) {
log.info("onChange called");
if (!accessPoints || !this.wifiDataPromise) {
if (!accessPoints || !this._wifiDataPromise) {
return;
}
@ -582,10 +661,10 @@ class RegionDetector {
this.wifiService = null;
}
if (this.wifiDataPromise) {
if (this._wifiDataPromise) {
let data = LocationHelper.formatWifiAccessPoints(accessPoints);
this.wifiDataPromise(data);
this.wifiDataPromise = null;
this._wifiDataPromise(data);
this._wifiDataPromise = null;
}
}
}

View File

@ -5,11 +5,13 @@ const { AppConstants } = ChromeUtils.import(
);
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
const { Region } = ChromeUtils.import("resource://gre/modules/Region.jsm");
const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
const { TestUtils } = ChromeUtils.import(
"resource://testing-common/TestUtils.jsm"
);
const REGION_PREF = "browser.region.network.url";
const INTERVAL_PREF = "browser.region.update.interval";
const RESPONSE_DELAY = 500;
const RESPONSE_TIMEOUT = 100;
@ -82,11 +84,9 @@ add_task(async function test_mismatched_probe() {
probeHistogram.clear();
}
histogram.clear();
Region._home = null;
Services.prefs.setCharPref(
REGION_PREF,
'data:application/json,{"country_code": "AU"}'
);
setNetworkRegion("AU");
await Region._fetchRegion();
Assert.equal(Region.home, "AU", "Should have correct region");
checkTelemetry(Region.TELEMETRY.SUCCESS);
@ -116,6 +116,33 @@ add_task(async function test_location() {
await new Promise(r => srv.stop(r));
});
add_task(async function test_update() {
Region._home = null;
setNetworkRegion("FR");
await Region._fetchRegion();
Assert.equal(Region.home, "FR", "Should have correct region");
setNetworkRegion("DE");
await Region._fetchRegion();
Assert.equal(Region.home, "FR", "Shouldnt have changed yet");
// Thie first fetchRegion will set the prefs to determine when
// to update the home region, we need to do 2 fetchRegions to test
// it isnt updating when it shouldnt.
await Region._fetchRegion();
Assert.equal(Region.home, "FR", "Shouldnt have changed yet again");
Services.prefs.setIntPref(INTERVAL_PREF, 1);
/* eslint-disable mozilla/no-arbitrary-setTimeout */
await new Promise(resolve => setTimeout(resolve, 1100));
await Region._fetchRegion();
Assert.equal(Region.home, "DE", "Should have updated now");
});
function setNetworkRegion(region) {
Services.prefs.setCharPref(
REGION_PREF,
`data:application/json,{"country_code": "${region}"}`
);
}
function useHttpServer(pref) {
let server = new HttpServer();
server.start(-1);