diff options
-rw-r--r-- | application/basilisk/app/profile/basilisk.js | 12 | ||||
-rw-r--r-- | modules/libpref/init/all.js | 3 | ||||
-rw-r--r-- | toolkit/components/search/current/nsSearchService.js | 436 | ||||
-rw-r--r-- | toolkit/components/search/orginal/nsSearchService.js | 419 | ||||
-rw-r--r-- | toolkit/components/urlformatter/nsURLFormatter.js | 9 |
5 files changed, 14 insertions, 865 deletions
diff --git a/application/basilisk/app/profile/basilisk.js b/application/basilisk/app/profile/basilisk.js index fc3b4546b..c7b2852dd 100644 --- a/application/basilisk/app/profile/basilisk.js +++ b/application/basilisk/app/profile/basilisk.js @@ -346,18 +346,6 @@ pref("browser.search.order.1", "chrome://browser-region/locale/re pref("browser.search.order.2", "chrome://browser-region/locale/region.properties"); pref("browser.search.order.3", "chrome://browser-region/locale/region.properties"); -// Market-specific search defaults -// This is disabled globally, and then enabled for individual locales -// in firefox-l10n.js (eg. it's enabled for en-US). -pref("browser.search.geoSpecificDefaults", false); -pref("browser.search.geoSpecificDefaults.url", "https://search.services.mozilla.com/1/%APP%/%VERSION%/%CHANNEL%/%LOCALE%/%REGION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%"); - -// US specific default (used as a fallback if the geoSpecificDefaults request fails). -pref("browser.search.defaultenginename.US", "data:text/plain,browser.search.defaultenginename.US=Google"); -pref("browser.search.order.US.1", "data:text/plain,browser.search.order.US.1=Google"); -pref("browser.search.order.US.2", "data:text/plain,browser.search.order.US.2=Yahoo"); -pref("browser.search.order.US.3", "data:text/plain,browser.search.order.US.3=Bing"); - // search bar results always open in a new tab pref("browser.search.openintab", false); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 10f9b994a..56dbba31e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5233,9 +5233,6 @@ pref("browser.search.update.interval", 21600); pref("browser.search.suggest.enabled", true); pref("browser.search.reset.enabled", false); pref("browser.search.reset.whitelist", ""); -pref("browser.search.geoSpecificDefaults", false); -pref("browser.search.geoip.url", "https://location.services.mozilla.com/v1/country?key=%MOZILLA_API_KEY%"); -pref("browser.search.geoip.timeout", 3000); #ifdef MOZ_OFFICIAL_BRANDING // {moz:official} expands to "official" diff --git a/toolkit/components/search/current/nsSearchService.js b/toolkit/components/search/current/nsSearchService.js index db90e5150..2ea9384f5 100644 --- a/toolkit/components/search/current/nsSearchService.js +++ b/toolkit/components/search/current/nsSearchService.js @@ -388,401 +388,6 @@ function isPartnerBuild() { return false; } -// Method to determine if we should be using geo-specific defaults -function geoSpecificDefaultsEnabled() { - return Services.prefs.getBoolPref("browser.search.geoSpecificDefaults", false); -} - -// Some notes on countryCode and region prefs: -// * A "countryCode" pref is set via a geoip lookup. It always reflects the -// result of that geoip request. -// * A "region" pref, once set, is the region actually used for search. In -// most cases it will be identical to the countryCode pref. -// * The value of "region" and "countryCode" will only not agree in one edge -// case - 34/35 users who have previously been configured to use US defaults -// based purely on a timezone check will have "region" forced to US, -// regardless of what countryCode geoip returns. -// * We may want to know if we are in the US before we have *either* -// countryCode or region - in which case we fallback to a timezone check, -// but we don't persist that value anywhere in the expectation we will -// eventually get a countryCode/region. - -// A method that "migrates" prefs if necessary. -function migrateRegionPrefs() { - // If we already have a "region" pref there's nothing to do. - if (Services.prefs.prefHasUserValue("browser.search.region")) { - return; - } - - // If we have 'isUS' but no 'countryCode' then we are almost certainly - // a profile from Fx 34/35 that set 'isUS' based purely on a timezone - // check. If this said they were US, we force region to be US. - // (But if isUS was false, we leave region alone - we will do a geoip request - // and set the region accordingly) - try { - if (Services.prefs.getBoolPref("browser.search.isUS") && - !Services.prefs.prefHasUserValue("browser.search.countryCode")) { - Services.prefs.setCharPref("browser.search.region", "US"); - } - } catch (ex) { - // no isUS pref, nothing to do. - } - // If we have a countryCode pref but no region pref, just force region - // to be the countryCode. - try { - let countryCode = Services.prefs.getCharPref("browser.search.countryCode"); - if (!Services.prefs.prefHasUserValue("browser.search.region")) { - Services.prefs.setCharPref("browser.search.region", countryCode); - } - } catch (ex) { - // no countryCode pref, nothing to do. - } -} - -// A method to determine if we are in the United States (US) for the search -// service. -// It uses a browser.search.region pref (which typically comes from a geoip -// request) or if that doesn't exist, falls back to a hacky timezone check. -function getIsUS() { - // Regardless of the region or countryCode, non en-US builds are not - // considered to be in the US from the POV of the search service. - if (getLocale() != "en-US") { - return false; - } - - // If we've got a region pref, trust it. - try { - return Services.prefs.getCharPref("browser.search.region") == "US"; - } catch (e) {} - - // So we are en-US but have no region pref - fallback to hacky timezone check. - let isNA = isUSTimezone(); - LOG("getIsUS() fell back to a timezone check with the result=" + isNA); - return isNA; -} - -// Helper method to modify preference keys with geo-specific modifiers, if needed. -function getGeoSpecificPrefName(basepref) { - if (!geoSpecificDefaultsEnabled() || isPartnerBuild()) - return basepref; - if (getIsUS()) - return basepref + ".US"; - return basepref; -} - -// 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 less hacky method that tries to determine our country-code via an XHR -// geoip lookup. -// If this succeeds and we are using an en-US locale, we set the pref used by -// the hacky method above, so isUS() can avoid the hacky timezone method. -// If it fails we don't touch that pref so isUS() does its normal thing. -var ensureKnownCountryCode = Task.async(function* (ss) { - // If we have a country-code already stored in our prefs we trust it. - let countryCode = Services.prefs.getCharPref("browser.search.countryCode", ""); - if (!countryCode) { - // We don't have it cached, so fetch it. fetchCountryCode() will call - // storeCountryCode if it gets a result (even if that happens after the - // promise resolves) and fetchRegionDefault. - yield fetchCountryCode(ss); - } else { - // if nothing to do, return early. - if (!geoSpecificDefaultsEnabled()) - return; - - let expir = ss.getGlobalAttr("searchDefaultExpir") || 0; - if (expir > Date.now()) { - // The territory default we have already fetched hasn't expired yet. - // If we have a default engine or a list of visible default engines - // saved, the hashes should be valid, verify them now so that we can - // refetch if they have been tampered with. - let defaultEngine = ss.getVerifiedGlobalAttr("searchDefault"); - let visibleDefaultEngines = ss.getVerifiedGlobalAttr("visibleDefaultEngines"); - if ((defaultEngine || defaultEngine === undefined) && - (visibleDefaultEngines || visibleDefaultEngines === undefined)) { - // No geo defaults, or valid hashes; nothing to do. - return; - } - } - - yield new Promise(resolve => { - let timeoutMS = Services.prefs.getIntPref("browser.search.geoip.timeout"); - let timerId = setTimeout(() => { - timerId = null; - resolve(); - }, timeoutMS); - - let callback = () => { - clearTimeout(timerId); - resolve(); - }; - fetchRegionDefault(ss).then(callback).catch(err => { - Components.utils.reportError(err); - callback(); - }); - }); - } - - // If gInitialized is true then the search service was forced to perform - // a sync initialization during our XHRs - capture this via telemetry. - Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_CAUSED_SYNC_INIT").add(gInitialized); -}); - -// Store the result of the geoip request as well as any other values and -// telemetry which depend on it. -function storeCountryCode(cc) { - // Set the country-code itself. - Services.prefs.setCharPref("browser.search.countryCode", cc); - // And set the region pref if we don't already have a value. - if (!Services.prefs.prefHasUserValue("browser.search.region")) { - Services.prefs.setCharPref("browser.search.region", cc); - } - // and telemetry... - let isTimezoneUS = isUSTimezone(); - if (cc == "US" && !isTimezoneUS) { - Services.telemetry.getHistogramById("SEARCH_SERVICE_US_COUNTRY_MISMATCHED_TIMEZONE").add(1); - } - if (cc != "US" && isTimezoneUS) { - 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 = Services.sysinfo.get("countryCode"); - if (platformCC) { - let probeUSMismatched, probeNonUSMismatched; - switch (Services.appinfo.OS) { - case "Darwin": - probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX"; - probeNonUSMismatched = "SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX"; - break; - case "WINNT": - 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 (cc == "US" || platformCC == "US") { - // one of the 2 said US, so record if they are the same. - Services.telemetry.getHistogramById(probeUSMismatched).add(cc != platformCC); - } else { - // different country - record if they are the same - Services.telemetry.getHistogramById(probeNonUSMismatched).add(cc != platformCC); - } - } - } -} - -// Get the country we are in via a XHR geoip request. -function fetchCountryCode(ss) { - // values for the SEARCH_SERVICE_COUNTRY_FETCH_RESULT 'enum' telemetry probe. - const TELEMETRY_RESULT_ENUM = { - SUCCESS: 0, - SUCCESS_WITHOUT_DATA: 1, - XHRTIMEOUT: 2, - ERROR: 3, - // Note that we expect to add finer-grained error types here later (eg, - // dns error, network error, ssl error, etc) with .ERROR remaining as the - // generic catch-all that doesn't fit into other categories. - }; - let endpoint = Services.urlFormatter.formatURLPref("browser.search.geoip.url"); - LOG("_fetchCountryCode starting with endpoint " + endpoint); - // As an escape hatch, no endpoint means no geoip. - if (!endpoint) { - return Promise.resolve(); - } - let startTime = Date.now(); - return new Promise(resolve => { - // Instead of using a timeout on the xhr object itself, we simulate one - // using a timer and let the XHR request complete. This allows us to - // capture reliable telemetry on what timeout value should actually be - // used to ensure most users don't see one while not making it so large - // that many users end up doing a sync init of the search service and thus - // would see the jank that implies. - // (Note we do actually use a timeout on the XHR, but that's set to be a - // large value just incase the request never completes - we don't want the - // XHR object to live forever) - let timeoutMS = Services.prefs.getIntPref("browser.search.geoip.timeout"); - let geoipTimeoutPossible = true; - let timerId = setTimeout(() => { - LOG("_fetchCountryCode: timeout fetching country information"); - if (geoipTimeoutPossible) - Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_TIMEOUT").add(1); - timerId = null; - resolve(); - }, timeoutMS); - - let resolveAndReportSuccess = (result, reason) => { - // Even if we timed out, we want to save the country code and everything - // related so next startup sees the value and doesn't retry this dance. - if (result) { - storeCountryCode(result); - } - Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_RESULT").add(reason); - - // This notification is just for tests... - Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "geoip-lookup-xhr-complete"); - - if (timerId) { - Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_TIMEOUT").add(0); - geoipTimeoutPossible = false; - } - - let callback = () => { - // If we've already timed out then we've already resolved the promise, - // so there's nothing else to do. - if (timerId == null) { - return; - } - clearTimeout(timerId); - resolve(); - }; - - if (result && geoSpecificDefaultsEnabled()) { - fetchRegionDefault(ss).then(callback).catch(err => { - Components.utils.reportError(err); - callback(); - }); - } else { - callback(); - } - }; - - let request = new XMLHttpRequest(); - // This notification is just for tests... - Services.obs.notifyObservers(request, SEARCH_SERVICE_TOPIC, "geoip-lookup-xhr-starting"); - request.timeout = 100000; // 100 seconds as the last-chance fallback - request.onload = function(event) { - let took = Date.now() - startTime; - let cc = event.target.response && event.target.response.country_code; - LOG("_fetchCountryCode got success response in " + took + "ms: " + cc); - Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS").add(took); - let reason = cc ? TELEMETRY_RESULT_ENUM.SUCCESS : TELEMETRY_RESULT_ENUM.SUCCESS_WITHOUT_DATA; - resolveAndReportSuccess(cc, reason); - }; - request.ontimeout = function(event) { - LOG("_fetchCountryCode: XHR finally timed-out fetching country information"); - resolveAndReportSuccess(null, TELEMETRY_RESULT_ENUM.XHRTIMEOUT); - }; - request.onerror = function(event) { - LOG("_fetchCountryCode: failed to retrieve country information"); - resolveAndReportSuccess(null, TELEMETRY_RESULT_ENUM.ERROR); - }; - request.open("POST", endpoint, true); - request.setRequestHeader("Content-Type", "application/json"); - request.responseType = "json"; - request.send("{}"); - }); -} - -// This will make an HTTP request to a Mozilla server that will return -// JSON data telling us what engine should be set as the default for -// the current region, and how soon we should check again. -// -// The optional cohort value returned by the server is to be kept locally -// and sent to the server the next time we ping it. It lets the server -// identify profiles that have been part of a specific experiment. -// -// This promise may take up to 100s to resolve, it's the caller's -// responsibility to ensure with a timer that we are not going to -// block the async init for too long. -var fetchRegionDefault = (ss) => new Promise(resolve => { - let urlTemplate = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF) - .getCharPref("geoSpecificDefaults.url"); - let endpoint = Services.urlFormatter.formatURL(urlTemplate); - - // As an escape hatch, no endpoint means no region specific defaults. - if (!endpoint) { - resolve(); - return; - } - - // Append the optional cohort value. - const cohortPref = "browser.search.cohort"; - let cohort = Services.prefs.getCharPref(cohortPref, ""); - if (cohort) - endpoint += "/" + cohort; - - LOG("fetchRegionDefault starting with endpoint " + endpoint); - - let startTime = Date.now(); - let request = new XMLHttpRequest(); - request.timeout = 100000; // 100 seconds as the last-chance fallback - request.onload = function(event) { - let took = Date.now() - startTime; - - let status = event.target.status; - if (status != 200) { - LOG("fetchRegionDefault failed with HTTP code " + status); - let retryAfter = request.getResponseHeader("retry-after"); - if (retryAfter) { - ss.setGlobalAttr("searchDefaultExpir", Date.now() + retryAfter * 1000); - } - resolve(); - return; - } - - let response = event.target.response || {}; - LOG("received " + response.toSource()); - - if (response.cohort) { - Services.prefs.setCharPref(cohortPref, response.cohort); - } else { - Services.prefs.clearUserPref(cohortPref); - } - - if (response.settings && response.settings.searchDefault) { - let defaultEngine = response.settings.searchDefault; - ss.setVerifiedGlobalAttr("searchDefault", defaultEngine); - LOG("fetchRegionDefault saved searchDefault: " + defaultEngine); - } - - if (response.settings && response.settings.visibleDefaultEngines) { - let visibleDefaultEngines = response.settings.visibleDefaultEngines; - let string = visibleDefaultEngines.join(","); - ss.setVerifiedGlobalAttr("visibleDefaultEngines", string); - LOG("fetchRegionDefault saved visibleDefaultEngines: " + string); - } - - let interval = response.interval || SEARCH_GEO_DEFAULT_UPDATE_INTERVAL; - let milliseconds = interval * 1000; // |interval| is in seconds. - ss.setGlobalAttr("searchDefaultExpir", Date.now() + milliseconds); - - LOG("fetchRegionDefault got success response in " + took + "ms"); - resolve(); - }; - request.ontimeout = function(event) { - LOG("fetchRegionDefault: XHR finally timed-out"); - resolve(); - }; - request.onerror = function(event) { - LOG("fetchRegionDefault: failed to retrieve territory default information"); - resolve(); - }; - request.open("GET", endpoint, true); - request.setRequestHeader("Content-Type", "application/json"); - request.responseType = "json"; - request.send(); -}); - function getVerificationHash(aName) { let disclaimer = "By modifying this file, I agree that I am doing so " + "only within $appName itself, using official, user-driven search " + @@ -2685,7 +2290,6 @@ SearchService.prototype = { _syncInit: function SRCH_SVC__syncInit() { LOG("_syncInit start"); this._initStarted = true; - migrateRegionPrefs(); let cache = this._readCacheFile(); if (cache.metaData) @@ -2720,8 +2324,6 @@ SearchService.prototype = { _asyncInit: Task.async(function* () { LOG("_asyncInit start"); - migrateRegionPrefs(); - // See if we have a cache file so we don't have to parse a bunch of XML. let cache = {}; // Not using checkForSyncCompletion here because we want to ensure we @@ -2733,14 +2335,6 @@ SearchService.prototype = { this._metaData = cache.metaData; try { - yield checkForSyncCompletion(ensureKnownCountryCode(this)); - } catch (ex) { - if (ex.result == Cr.NS_ERROR_ALREADY_INITIALIZED) { - throw ex; - } - LOG("_asyncInit: failure determining country code: " + ex); - } - try { yield checkForSyncCompletion(this._asyncLoadEngines(cache)); } catch (ex) { if (ex.result == Cr.NS_ERROR_ALREADY_INITIALIZED) { @@ -2800,9 +2394,8 @@ SearchService.prototype = { let defaultPrefB = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF); let nsIPLS = Ci.nsIPrefLocalizedString; - let defPref = getGeoSpecificPrefName("defaultenginename"); try { - defaultEngine = defaultPrefB.getComplexValue(defPref, nsIPLS).data; + defaultEngine = defaultPrefB.getComplexValue("defaultenginename", nsIPLS).data; } catch (ex) { // If the default pref is invalid (e.g. an add-on set it to a bogus value) // getEngineByName will just return null, which is the best we can do. @@ -3117,11 +2710,7 @@ SearchService.prototype = { if (!gInitialized && cache.metaData) this._metaData = cache.metaData; - yield ensureKnownCountryCode(this); - // Due to the HTTP requests done by ensureKnownCountryCode, it's possible that - // at this point a synchronous init has been forced by other code. - if (!gInitialized) - yield this._asyncLoadEngines(cache); + yield this._asyncLoadEngines(cache); // Typically we'll re-init as a result of a pref observer, // so signal to 'callers' that we're done. @@ -3647,14 +3236,7 @@ SearchService.prototype = { // Fallback to building a list based on the regions in the JSON if (!engineNames || !engineNames.length) { - let region; - if (Services.prefs.prefHasUserValue("browser.search.region")) { - region = Services.prefs.getCharPref("browser.search.region"); - } - if (!region || !(region in searchSettings)) { - region = "default"; - } - engineNames = searchSettings[region]["visibleDefaultEngines"]; + engineNames = searchSettings["default"]["visibleDefaultEngines"]; } for (let name of engineNames) { @@ -3797,10 +3379,8 @@ SearchService.prototype = { } catch (e) { } - let prefNameBase = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "order"); while (true) { - prefName = prefNameBase + "." + (++i); - engineName = getLocalizedPref(prefName); + engineName = getLocalizedPref(BROWSER_SEARCH_PREF + "order." + (++i)); if (!engineName) break; @@ -3934,10 +3514,8 @@ SearchService.prototype = { } // Now look through the "browser.search.order" branch. - let prefNameBase = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "order"); for (var j = 1; ; j++) { - let prefName = prefNameBase + "." + j; - engineName = getLocalizedPref(prefName); + engineName = getLocalizedPref(BROWSER_SEARCH_PREF + "order." + j); if (!engineName) break; @@ -4291,11 +3869,9 @@ SearchService.prototype = { } catch (e) {} } - let prefNameBase = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "order"); let i = 0; while (!sendSubmissionURL) { - let prefName = prefNameBase + "." + (++i); - let engineName = getLocalizedPref(prefName); + let engineName = getLocalizedPref(BROWSER_SEARCH_PREF + "order." + (++i)); if (!engineName) break; if (result.name == engineName) { diff --git a/toolkit/components/search/orginal/nsSearchService.js b/toolkit/components/search/orginal/nsSearchService.js index 1cec5f966..760f13e72 100644 --- a/toolkit/components/search/orginal/nsSearchService.js +++ b/toolkit/components/search/orginal/nsSearchService.js @@ -416,387 +416,6 @@ function isPartnerBuild() { return false; } -// Method to determine if we should be using geo-specific defaults -function geoSpecificDefaultsEnabled() { - return Services.prefs.getBoolPref("browser.search.geoSpecificDefaults", false); -} - -// Some notes on countryCode and region prefs: -// * A "countryCode" pref is set via a geoip lookup. It always reflects the -// result of that geoip request. -// * A "region" pref, once set, is the region actually used for search. In -// most cases it will be identical to the countryCode pref. -// * The value of "region" and "countryCode" will only not agree in one edge -// case - 34/35 users who have previously been configured to use US defaults -// based purely on a timezone check will have "region" forced to US, -// regardless of what countryCode geoip returns. -// * We may want to know if we are in the US before we have *either* -// countryCode or region - in which case we fallback to a timezone check, -// but we don't persist that value anywhere in the expectation we will -// eventually get a countryCode/region. - -// A method that "migrates" prefs if necessary. -function migrateRegionPrefs() { - // If we already have a "region" pref there's nothing to do. - if (Services.prefs.prefHasUserValue("browser.search.region")) { - return; - } - - // If we have 'isUS' but no 'countryCode' then we are almost certainly - // a profile from Fx 34/35 that set 'isUS' based purely on a timezone - // check. If this said they were US, we force region to be US. - // (But if isUS was false, we leave region alone - we will do a geoip request - // and set the region accordingly) - try { - if (Services.prefs.getBoolPref("browser.search.isUS") && - !Services.prefs.prefHasUserValue("browser.search.countryCode")) { - Services.prefs.setCharPref("browser.search.region", "US"); - } - } catch (ex) { - // no isUS pref, nothing to do. - } - // If we have a countryCode pref but no region pref, just force region - // to be the countryCode. - try { - let countryCode = Services.prefs.getCharPref("browser.search.countryCode"); - if (!Services.prefs.prefHasUserValue("browser.search.region")) { - Services.prefs.setCharPref("browser.search.region", countryCode); - } - } catch (ex) { - // no countryCode pref, nothing to do. - } -} - -// A method to determine if we are in the United States (US) for the search -// service. -// It uses a browser.search.region pref (which typically comes from a geoip -// request) or if that doesn't exist, falls back to a hacky timezone check. -function getIsUS() { - // Regardless of the region or countryCode, non en-US builds are not - // considered to be in the US from the POV of the search service. - if (getLocale() != "en-US") { - return false; - } - - // If we've got a region pref, trust it. - try { - return Services.prefs.getCharPref("browser.search.region") == "US"; - } catch(e) {} - - // So we are en-US but have no region pref - fallback to hacky timezone check. - let isNA = isUSTimezone(); - LOG("getIsUS() fell back to a timezone check with the result=" + isNA); - return isNA; -} - -// Helper method to modify preference keys with geo-specific modifiers, if needed. -function getGeoSpecificPrefName(basepref) { - if (!geoSpecificDefaultsEnabled() || isPartnerBuild()) - return basepref; - if (getIsUS()) - return basepref + ".US"; - return basepref; -} - -// 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 less hacky method that tries to determine our country-code via an XHR -// geoip lookup. -// If this succeeds and we are using an en-US locale, we set the pref used by -// the hacky method above, so isUS() can avoid the hacky timezone method. -// If it fails we don't touch that pref so isUS() does its normal thing. -var ensureKnownCountryCode = Task.async(function* () { - // If we have a country-code already stored in our prefs we trust it. - let countryCode = Services.prefs.getCharPref("browser.search.countryCode", ""); - if (!countryCode) { - // We don't have it cached, so fetch it. fetchCountryCode() will call - // storeCountryCode if it gets a result (even if that happens after the - // promise resolves) and fetchRegionDefault. - yield fetchCountryCode(); - } else { - // if nothing to do, return early. - if (!geoSpecificDefaultsEnabled()) - return; - - let expir = engineMetadataService.getGlobalAttr("searchDefaultExpir") || 0; - if (expir > Date.now()) { - // The territory default we have already fetched hasn't expired yet. - // If we have a default engine or a list of visible default engines - // saved, the hashes should be valid, verify them now so that we can - // refetch if they have been tampered with. - let defaultEngine = engineMetadataService.getGlobalAttr("searchDefault"); - let visibleDefaultEngines = - engineMetadataService.getGlobalAttr("visibleDefaultEngines"); - if ((!defaultEngine || engineMetadataService.getGlobalAttr("searchDefaultHash") == getVerificationHash(defaultEngine)) && - (!visibleDefaultEngines || - engineMetadataService.getGlobalAttr("visibleDefaultEnginesHash") == getVerificationHash(visibleDefaultEngines))) { - // No geo defaults, or valid hashes; nothing to do. - return; - } - } - - yield new Promise(resolve => { - let timeoutMS = Services.prefs.getIntPref("browser.search.geoip.timeout"); - let timerId = setTimeout(() => { - timerId = null; - resolve(); - }, timeoutMS); - - let callback = () => { - clearTimeout(timerId); - resolve(); - }; - fetchRegionDefault().then(callback).catch(err => { - Components.utils.reportError(err); - callback(); - }); - }); - } -}); - -// Store the result of the geoip request as well as any other values and -// telemetry which depend on it. -function storeCountryCode(cc) { - // Set the country-code itself. - Services.prefs.setCharPref("browser.search.countryCode", cc); - // And set the region pref if we don't already have a value. - if (!Services.prefs.prefHasUserValue("browser.search.region")) { - Services.prefs.setCharPref("browser.search.region", cc); - } - // and telemetry... - let isTimezoneUS = isUSTimezone(); - // 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 = Services.sysinfo.get("countryCode"); - if (platformCC) { - let probeUSMismatched, probeNonUSMismatched; - switch (Services.appinfo.OS) { - case "Darwin": - probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX"; - probeNonUSMismatched = "SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX"; - break; - case "WINNT": - 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; - } - } -} - -// Get the country we are in via a XHR geoip request. -function fetchCountryCode() { - // values for the SEARCH_SERVICE_COUNTRY_FETCH_RESULT 'enum' telemetry probe. - const TELEMETRY_RESULT_ENUM = { - SUCCESS: 0, - SUCCESS_WITHOUT_DATA: 1, - XHRTIMEOUT: 2, - ERROR: 3, - // Note that we expect to add finer-grained error types here later (eg, - // dns error, network error, ssl error, etc) with .ERROR remaining as the - // generic catch-all that doesn't fit into other categories. - }; - let endpoint = Services.urlFormatter.formatURLPref("browser.search.geoip.url"); - LOG("_fetchCountryCode starting with endpoint " + endpoint); - // As an escape hatch, no endpoint means no geoip. - if (!endpoint) { - return Promise.resolve(); - } - let startTime = Date.now(); - return new Promise(resolve => { - // Instead of using a timeout on the xhr object itself, we simulate one - // using a timer and let the XHR request complete. This allows us to - // capture reliable telemetry on what timeout value should actually be - // used to ensure most users don't see one while not making it so large - // that many users end up doing a sync init of the search service and thus - // would see the jank that implies. - // (Note we do actually use a timeout on the XHR, but that's set to be a - // large value just incase the request never completes - we don't want the - // XHR object to live forever) - let timeoutMS = Services.prefs.getIntPref("browser.search.geoip.timeout"); - let geoipTimeoutPossible = true; - let timerId = setTimeout(() => { - LOG("_fetchCountryCode: timeout fetching country information"); - timerId = null; - resolve(); - }, timeoutMS); - - let resolveAndReportSuccess = (result, reason) => { - // Even if we timed out, we want to save the country code and everything - // related so next startup sees the value and doesn't retry this dance. - if (result) { - storeCountryCode(result); - } - - // This notification is just for tests... - Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "geoip-lookup-xhr-complete"); - - if (timerId) { - geoipTimeoutPossible = false; - } - - let callback = () => { - // If we've already timed out then we've already resolved the promise, - // so there's nothing else to do. - if (timerId == null) { - return; - } - clearTimeout(timerId); - resolve(); - }; - - if (result && geoSpecificDefaultsEnabled()) { - fetchRegionDefault().then(callback).catch(err => { - Components.utils.reportError(err); - callback(); - }); - } else { - callback(); - } - }; - - let request = new XMLHttpRequest(); - // This notification is just for tests... - Services.obs.notifyObservers(request, SEARCH_SERVICE_TOPIC, "geoip-lookup-xhr-starting"); - request.timeout = 100000; // 100 seconds as the last-chance fallback - request.onload = function(event) { - let took = Date.now() - startTime; - let cc = event.target.response && event.target.response.country_code; - LOG("_fetchCountryCode got success response in " + took + "ms: " + cc); - let reason = cc ? TELEMETRY_RESULT_ENUM.SUCCESS : TELEMETRY_RESULT_ENUM.SUCCESS_WITHOUT_DATA; - resolveAndReportSuccess(cc, reason); - }; - request.ontimeout = function(event) { - LOG("_fetchCountryCode: XHR finally timed-out fetching country information"); - resolveAndReportSuccess(null, TELEMETRY_RESULT_ENUM.XHRTIMEOUT); - }; - request.onerror = function(event) { - LOG("_fetchCountryCode: failed to retrieve country information"); - resolveAndReportSuccess(null, TELEMETRY_RESULT_ENUM.ERROR); - }; - request.open("POST", endpoint, true); - request.setRequestHeader("Content-Type", "application/json"); - request.responseType = "json"; - request.send("{}"); - }); -} - -// This will make an HTTP request to a Mozilla server that will return -// JSON data telling us what engine should be set as the default for -// the current region, and how soon we should check again. -// -// The optional cohort value returned by the server is to be kept locally -// and sent to the server the next time we ping it. It lets the server -// identify profiles that have been part of a specific experiment. -// -// This promise may take up to 100s to resolve, it's the caller's -// responsibility to ensure with a timer that we are not going to -// block the async init for too long. -var fetchRegionDefault = () => new Promise(resolve => { - let urlTemplate = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF) - .getCharPref("geoSpecificDefaults.url"); - let endpoint = Services.urlFormatter.formatURL(urlTemplate); - - // As an escape hatch, no endpoint means no region specific defaults. - if (!endpoint) { - resolve(); - return; - } - - // Append the optional cohort value. - const cohortPref = "browser.search.cohort"; - let cohort = Services.prefs.getCharPref(cohortPref, ""); - if (cohort) - endpoint += "/" + cohort; - - LOG("fetchRegionDefault starting with endpoint " + endpoint); - - let startTime = Date.now(); - let request = new XMLHttpRequest(); - request.timeout = 100000; // 100 seconds as the last-chance fallback - request.onload = function(event) { - let took = Date.now() - startTime; - - let status = event.target.status; - if (status != 200) { - LOG("fetchRegionDefault failed with HTTP code " + status); - let retryAfter = request.getResponseHeader("retry-after"); - if (retryAfter) { - engineMetadataService.setGlobalAttr("searchDefaultExpir", - Date.now() + retryAfter * 1000); - } - resolve(); - return; - } - - let response = event.target.response || {}; - LOG("received " + response.toSource()); - - if (response.cohort) { - Services.prefs.setCharPref(cohortPref, response.cohort); - } else { - Services.prefs.clearUserPref(cohortPref); - } - - if (response.settings && response.settings.searchDefault) { - let defaultEngine = response.settings.searchDefault; - engineMetadataService.setGlobalAttr("searchDefault", defaultEngine); - let hash = getVerificationHash(defaultEngine); - LOG("fetchRegionDefault saved searchDefault: " + defaultEngine + - " with verification hash: " + hash); - engineMetadataService.setGlobalAttr("searchDefaultHash", hash); - } - - if (response.settings && response.settings.visibleDefaultEngines) { - let visibleDefaultEngines = response.settings.visibleDefaultEngines; - let string = visibleDefaultEngines.join(","); - engineMetadataService.setGlobalAttr("visibleDefaultEngines", string); - let hash = getVerificationHash(string); - LOG("fetchRegionDefault saved visibleDefaultEngines: " + string + - " with verification hash: " + hash); - engineMetadataService.setGlobalAttr("visibleDefaultEnginesHash", hash); - } - - let interval = response.interval || SEARCH_GEO_DEFAULT_UPDATE_INTERVAL; - let milliseconds = interval * 1000; // |interval| is in seconds. - engineMetadataService.setGlobalAttr("searchDefaultExpir", - Date.now() + milliseconds); - - LOG("fetchRegionDefault got success response in " + took + "ms"); - resolve(); - }; - request.ontimeout = function(event) { - LOG("fetchRegionDefault: XHR finally timed-out"); - resolve(); - }; - request.onerror = function(event) { - LOG("fetchRegionDefault: failed to retrieve territory default information"); - resolve(); - }; - request.open("GET", endpoint, true); - request.setRequestHeader("Content-Type", "application/json"); - request.responseType = "json"; - request.send(); -}); - function getVerificationHash(aName) { let disclaimer = "By modifying this file, I agree that I am doing so " + "only within $appName itself, using official, user-driven search " + @@ -2910,7 +2529,6 @@ SearchService.prototype = { _syncInit: function SRCH_SVC__syncInit() { LOG("_syncInit start"); this._initStarted = true; - migrateRegionPrefs(); try { this._syncLoadEngines(); } catch (ex) { @@ -2935,15 +2553,9 @@ SearchService.prototype = { * succeeds. */ _asyncInit: function SRCH_SVC__asyncInit() { - migrateRegionPrefs(); return Task.spawn(function() { LOG("_asyncInit start"); try { - yield checkForSyncCompletion(ensureKnownCountryCode()); - } catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) { - LOG("_asyncInit: failure determining country code: " + ex); - } - try { yield checkForSyncCompletion(this._asyncLoadEngines()); } catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) { this._initRV = Cr.NS_ERROR_FAILURE; @@ -2982,9 +2594,8 @@ SearchService.prototype = { let defaultPrefB = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF); let nsIPLS = Ci.nsIPrefLocalizedString; - let defPref = getGeoSpecificPrefName("defaultenginename"); try { - defaultEngine = defaultPrefB.getComplexValue(defPref, nsIPLS).data; + defaultEngine = defaultPrefB.getComplexValue("defaultenginename", nsIPLS).data; } catch (ex) { // If the default pref is invalid (e.g. an add-on set it to a bogus value) // getEngineByName will just return null, which is the best we can do. @@ -3329,12 +2940,7 @@ SearchService.prototype = { try { LOG("Restarting engineMetadataService"); yield engineMetadataService.init(); - yield ensureKnownCountryCode(); - - // Due to the HTTP requests done by ensureKnownCountryCode, it's possible that - // at this point a synchronous init has been forced by other code. - if (!gInitialized) - yield this._asyncLoadEngines(); + yield this._asyncLoadEngines(); // Typically we'll re-init as a result of a pref observer, // so signal to 'callers' that we're done. @@ -3841,10 +3447,8 @@ SearchService.prototype = { } catch (e) { } - let prefNameBase = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "order"); while (true) { - prefName = prefNameBase + "." + (++i); - engineName = getLocalizedPref(prefName); + engineName = getLocalizedPref(BROWSER_SEARCH_PREF + "order." + (++i)); if (!engineName) break; @@ -3977,10 +3581,8 @@ SearchService.prototype = { } // Now look through the "browser.search.order" branch. - let prefNameBase = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "order"); for (var j = 1; ; j++) { - let prefName = prefNameBase + "." + j; - engineName = getLocalizedPref(prefName); + engineName = getLocalizedPref(BROWSER_SEARCH_PREF + "order." + j); if (!engineName) break; @@ -4193,8 +3795,7 @@ SearchService.prototype = { get defaultEngine() { this._ensureInitialized(); if (!this._defaultEngine) { - let defPref = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "defaultenginename"); - let defaultEngine = this.getEngineByName(getLocalizedPref(defPref, "")) + let defaultEngine = this.getEngineByName(getLocalizedPref(BROWSER_SEARCH_PREF + "defaultenginename", "")) if (!defaultEngine) defaultEngine = this._getSortedEngines(false)[0] || null; this._defaultEngine = defaultEngine; @@ -4221,18 +3822,16 @@ SearchService.prototype = { this._defaultEngine = newDefaultEngine; - let defPref = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "defaultenginename"); - // If we change the default engine in the future, that change should impact // users who have switched away from and then back to the build's "default" // engine. So clear the user pref when the defaultEngine is set to the // build's default engine, so that the defaultEngine getter falls back to // whatever the default is. if (this._defaultEngine == this._originalDefaultEngine) { - Services.prefs.clearUserPref(defPref); + Services.prefs.clearUserPref(BROWSER_SEARCH_PREF + "defaultenginename"); } else { - setLocalizedPref(defPref, this._defaultEngine.name); + setLocalizedPref(BROWSER_SEARCH_PREF + "defaultenginename", this._defaultEngine.name); } notifyAction(this._defaultEngine, SEARCH_ENGINE_DEFAULT); @@ -4335,11 +3934,9 @@ SearchService.prototype = { } catch(e) {} } - let prefNameBase = getGeoSpecificPrefName(BROWSER_SEARCH_PREF + "order"); let i = 0; while (!sendSubmissionURL) { - let prefName = prefNameBase + "." + (++i); - let engineName = getLocalizedPref(prefName); + let engineName = getLocalizedPref(BROWSER_SEARCH_PREF + "order." + (++i)); if (!engineName) break; if (result.name == engineName) { diff --git a/toolkit/components/urlformatter/nsURLFormatter.js b/toolkit/components/urlformatter/nsURLFormatter.js index 970de0537..a4a8e1f47 100644 --- a/toolkit/components/urlformatter/nsURLFormatter.js +++ b/toolkit/components/urlformatter/nsURLFormatter.js @@ -91,15 +91,6 @@ nsURLFormatterService.prototype = { LOCALE: () => Cc["@mozilla.org/chrome/chrome-registry;1"]. getService(Ci.nsIXULChromeRegistry). getSelectedLocale('global'), - REGION: function() { - try { - // When the geoip lookup failed to identify the region, we fallback to - // the 'ZZ' region code to mean 'unknown'. - return Services.prefs.getCharPref("browser.search.region") || "ZZ"; - } catch(e) { - return "ZZ"; - } - }, VENDOR: function() { return this.appInfo.vendor; }, NAME: function() { return this.appInfo.name; }, ID: function() { return this.appInfo.ID; }, |