Skip to content

Commit

Permalink
Bug 1748923 - Show Search Engine Removal Infobar on restart or 5 seco…
Browse files Browse the repository at this point in the history
…nds idle. r=Standard8,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D136818
  • Loading branch information
mandysGit committed Feb 3, 2022
1 parent 8a44506 commit d28e21c
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 11 deletions.
3 changes: 3 additions & 0 deletions browser/app/profile/firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,9 @@ pref("browser.download.clearHistoryOnDelete", 0);
pref("browser.helperApps.showOpenOptionForPdfJS", true);
pref("browser.helperApps.showOpenOptionForViewableInternally", true);

// search engine removal URL
pref("browser.search.searchEngineRemoval", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/search-engine-removal");

// search engines URL
pref("browser.search.searchEnginesURL", "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");

Expand Down
51 changes: 51 additions & 0 deletions browser/base/content/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4278,6 +4278,57 @@ const BrowserSearch = {
var where = newWindowPref == 3 ? "tab" : "window";
openTrustedLinkIn(this.searchEnginesURL, where);
},

/**
* Infobar to notify the user's search engine has been removed
* and replaced with an application default search engine.
*
* @param {string} oldEngine
* name of the engine to be moved and replaced.
* @param {string} newEngine
* name of the application default engine to replaced the removed engine.
*/
removalOfSearchEngineNotificationBox(oldEngine, newEngine) {
let messageFragment = document.createDocumentFragment();
let message = document.createElement("span");
let link = document.createXULElement("label", {
is: "text-link",
});

link.href = Services.urlFormatter.formatURLPref(
"browser.search.searchEngineRemoval"
);
link.setAttribute("data-l10n-name", "remove-search-engine-article");
document.l10n.setAttributes(message, "remove-search-engine-message", {
oldEngine,
newEngine,
});

message.appendChild(link);
messageFragment.appendChild(message);

let button = [
{
"l10n-id": "remove-search-engine-button",
primary: true,
callback() {
const notificationBox = gNotificationBox.getNotificationWithValue(
"search-engine-removal"
);
gNotificationBox.removeNotification(notificationBox);
},
},
];

gNotificationBox.appendNotification(
"search-engine-removal",
{
label: messageFragment,
priority: gNotificationBox.PRIORITY_SYSTEM,
},
button
);
},
};

XPCOMUtils.defineConstant(this, "BrowserSearch", BrowserSearch);
Expand Down
1 change: 1 addition & 0 deletions browser/base/content/browser.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
persist="screenX screenY width height sizemode"
data-l10n-sync="true">
<head>
<link rel="localization" href="browser/search.ftl"/>
<link rel="localization" href="branding/brand.ftl"/>
<link rel="localization" href="browser/branding/sync-brand.ftl"/>
<link rel="localization" href="browser/branding/brandings.ftl"/>
Expand Down
8 changes: 8 additions & 0 deletions browser/locales/en-US/browser/search.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ searchbar-input =
searchbar-icon =
.tooltiptext = Search
## Infobar shown when search engine is removed and replaced.
## Variables
## $oldEngine (String) - the search engine to be removed.
## $newEngine (String) - the search engine to replace the removed search engine.

remove-search-engine-message = <strong>Your default search engine has been changed.</strong> { -brand-short-name } no longer supports { $oldEngine }. { $newEngine } is now your default search engine. To change to another default search engine, go to settings. <label data-l10n-name="remove-search-engine-article">Learn more</label>
remove-search-engine-button = OK
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ function submitHandler(request, response) {
}

add_task(async function setup() {
// This ensures the infobar is not shown in this test.
const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm");
sinon.stub(BrowserSearch, "removalOfSearchEngineNotificationBox");
// Ensure the initial init is complete.
await Services.search.init();

Expand Down Expand Up @@ -88,6 +91,7 @@ add_task(async function setup() {
await PlacesUtils.history.clear();
gCUITestUtils.removeSearchBar();
await settingsWritten;
sinon.restore();
});
});

Expand Down
112 changes: 101 additions & 11 deletions toolkit/components/search/SearchService.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,11 @@ SearchService.prototype = {
* An object representing the search engine settings.
*/
async _loadEngines(settings) {
// Get user's current settings and search engine before we load engines from
// config. These values will be compared after engines are loaded.
let prevMetaData = { ...settings?.metaData };
let prevCurrentEngine = prevMetaData.current;

logConsole.debug("_loadEngines: start");
let { engines, privateDefault } = await this._fetchEngineSelectorEngines();
this._setDefaultAndOrdersFromSelector(engines, privateDefault);
Expand Down Expand Up @@ -585,6 +590,23 @@ SearchService.prototype = {
this._loadEnginesMetadataFromSettings(settings.engines);

logConsole.debug("_loadEngines: done");

// If the defaultEngine has changed and the user's search settings are the
// same, notify user their engine has been removed.
let newCurrentEngine = this._getEngineDefault(false)?.name;

if (
prevCurrentEngine &&
newCurrentEngine !== prevCurrentEngine &&
prevMetaData &&
settings.metaData &&
!this._hasSettingsMetaDataChanged(prevMetaData, settings.metaData)
) {
this._showRemovalOfSearchEngineNotificationBox(
prevCurrentEngine,
newCurrentEngine
);
}
},

/**
Expand Down Expand Up @@ -660,8 +682,9 @@ SearchService.prototype = {

async _reloadEngines(settings) {
// Capture the current engine state, in case we need to notify below.
const prevCurrentEngine = this._currentEngine;
const prevPrivateEngine = this._currentPrivateEngine;
let prevCurrentEngine = this._currentEngine;
let prevPrivateEngine = this._currentPrivateEngine;
let prevMetaData = { ...settings?.metaData };

// Ensure that we don't set the useSavedOrder flag whilst we're doing this.
// This isn't a user action, so we shouldn't be switching it.
Expand Down Expand Up @@ -774,6 +797,21 @@ SearchService.prototype = {
// Now set the sort out the default engines and notify as appropriate.
this._currentEngine = null;
this._currentPrivateEngine = null;
// If the user's default is one of the private engines that is being removed,
// reset the stored setting, so that we correctly detect the change in
// in default.
if (
prevCurrentEngine &&
enginesToRemove.some(e => e.name == prevCurrentEngine.name)
) {
this._settings.setAttribute("current", "");
}
if (
prevPrivateEngine &&
enginesToRemove.some(e => e.name == prevPrivateEngine.name)
) {
this._settings.setAttribute("private", "");
}

this._setDefaultAndOrdersFromSelector(
originalConfigEngines,
Expand All @@ -795,7 +833,19 @@ SearchService.prototype = {
SearchUtils.MODIFIED_TYPE.DEFAULT_PRIVATE
);
}

if (
prevMetaData &&
settings.metaData &&
!this._hasSettingsMetaDataChanged(prevMetaData, settings.metaData)
) {
this._showRemovalOfSearchEngineNotificationBox(
prevCurrentEngine.name,
this.defaultEngine.name
);
}
}

if (
this._separatePrivateDefault &&
prevPrivateEngine &&
Expand Down Expand Up @@ -2143,11 +2193,7 @@ SearchService.prototype = {
// to pick a new current engine. As soon as we return it, this new
// current engine will become user-visible, so we should persist it.
// by calling the setter.
if (privateMode) {
this.defaultPrivateEngine = newDefault;
} else {
this.defaultEngine = newDefault;
}
this._setEngineDefault(privateMode, newDefault);

return this[currentEngineProp];
},
Expand All @@ -2163,7 +2209,6 @@ SearchService.prototype = {
* The appropriate search engine, or null if one could not be determined.
*/
_getEngineDefault(privateMode) {
this._ensureInitialized();
const currentEngineProp = privateMode
? "_currentPrivateEngine"
: "_currentEngine";
Expand All @@ -2175,7 +2220,7 @@ SearchService.prototype = {
// No default loaded, so find it from settings.
const attributeName = privateMode ? "private" : "current";
let name = this._settings.getAttribute(attributeName);
let engine = this.getEngineByName(name);
let engine = this._engines.get(name) || null;
if (
engine &&
(engine.isAppProvided ||
Expand Down Expand Up @@ -2210,7 +2255,6 @@ SearchService.prototype = {
* The search engine to select
*/
_setEngineDefault(privateMode, newEngine) {
this._ensureInitialized();
// Sometimes we get wrapped nsISearchEngine objects (external XPCOM callers),
// and sometimes we get raw Engine JS objects (callers in this file), so
// handle both.
Expand All @@ -2224,7 +2268,7 @@ SearchService.prototype = {
);
}

const newCurrentEngine = this.getEngineByName(newEngine.name);
const newCurrentEngine = this._engines.get(newEngine.name);
if (!newCurrentEngine) {
throw Components.Exception(
"Can't find engine in store!",
Expand Down Expand Up @@ -2295,18 +2339,22 @@ SearchService.prototype = {
},

get defaultEngine() {
this._ensureInitialized();
return this._getEngineDefault(false);
},

set defaultEngine(newEngine) {
this._ensureInitialized();
this._setEngineDefault(false, newEngine);
},

get defaultPrivateEngine() {
this._ensureInitialized();
return this._getEngineDefault(this._separatePrivateDefault);
},

set defaultPrivateEngine(newEngine) {
this._ensureInitialized();
if (!this._separatePrivateDefaultPrefValue) {
Services.prefs.setBoolPref(
SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault",
Expand Down Expand Up @@ -2824,6 +2872,48 @@ SearchService.prototype = {
"nsIObserver",
"nsITimerCallback",
]),

/**
* @param {object} prevMetaDataObj
* settings metaData Object
* @param {object} currentMetaDataObj
* settings metaData Object
* @returns {boolean}
* Returns true if the metaData objects have different properties values.
*/
_hasSettingsMetaDataChanged(prevMetaDataObj, currentMetaDataObj) {
let metaDataProperties = [
"locale",
"region",
"channel",
"experiment",
"distroID",
];

return metaDataProperties.some(
p => prevMetaDataObj?.[p] != currentMetaDataObj?.[p]
);
},

/**
* Shows an infobar to notify the user their default search engine has been
* removed and replaced by a new default search engine.
*
* @param {string} prevCurrentEngine
* The engine that was previously the default engine and is to be replaced.
* @param {string} newCurrentEngine
* The engine that will be the new the default engine.
*/
_showRemovalOfSearchEngineNotificationBox(
prevCurrentEngine,
newCurrentEngine
) {
let win = Services.wm.getMostRecentBrowserWindow();
win.BrowserSearch.removalOfSearchEngineNotificationBox(
prevCurrentEngine,
newCurrentEngine
);
},
};

var engineUpdateService = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
const kDefaultEngineName = "engine1";

add_task(async function setup() {
// Stub removal of search engine notification box.
// This ensures that BrowserSearch.win is not accessed in this test.
sinon.stub(
await Services.search.wrappedJSObject,
"_showRemovalOfSearchEngineNotificationBox"
);

useHttpServer();
await AddonTestUtils.promiseStartupManager();
await SearchTestUtils.useTestEngines("data1");
Expand Down

0 comments on commit d28e21c

Please sign in to comment.