Bug 1963600: Implement VPN Suggestions r=desktop-theme-reviewers,adw,dao

Differential Revision: https://phabricator.services.mozilla.com/D247284
This commit is contained in:
Daisuke Akatsuka
2025-05-07 23:36:48 +00:00
committed by dakatsuka.birchill@mozilla.com
parent c1e56ab969
commit c4be79e31b
5 changed files with 283 additions and 2 deletions

View File

@@ -265,7 +265,7 @@ class ProviderQuickSuggest extends UrlbarProvider {
let { result } = details;
// Delegate to the result's feature if there is one.
let feature = lazy.QuickSuggest.getFeatureByResult(details.result);
let feature = lazy.QuickSuggest.getFeatureByResult(result);
if (feature) {
feature.onEngagement(
queryContext,

View File

@@ -117,6 +117,22 @@ export class DynamicSuggestions extends SuggestProvider {
);
}
onEngagement(_queryContext, controller, details, _searchString) {
switch (details.selType) {
case "manage":
// "manage" is handled by UrlbarInput, no need to do anything here.
break;
case "dismiss":
let { result } = details;
lazy.QuickSuggest.dismissResult(result);
result.acknowledgeDismissalL10n = {
id: "firefox-suggest-dismissal-acknowledgment-one",
};
controller.removeResult(result);
break;
}
}
#makeExposureResult(suggestion, payload) {
// It doesn't really matter what kind of result we return since it won't be
// shown. Use a dynamic result since that kind of makes sense and there are

View File

@@ -21,6 +21,8 @@ prefs = ["browser.bookmarks.testing.skipDefaultBookmarksImport=true"]
["browser_quicksuggest_contextual_optin.js"]
["browser_quicksuggest_dynamicSuggestions.js"]
["browser_quicksuggest_fakespot.js"]
["browser_quicksuggest_indexes.js"]

View File

@@ -0,0 +1,252 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for dynamicSuggestions.
const REMOTE_SETTINGS_DATA = [
{
type: "dynamic-suggestions",
suggestion_type: "basic",
attachment: [
{
keywords: ["basic"],
data: {
result: {
payload: {
title: "basic",
url: "https://example.com/basic",
icon: "https://example.com/basic.svg",
},
},
},
},
],
},
{
type: "dynamic-suggestions",
suggestion_type: "shouldShowUrl",
attachment: [
{
keywords: ["shouldshowurl"],
data: {
result: {
payload: {
title: "shouldShowUrl",
url: "https://example.com/shouldShowUrl",
icon: "https://example.com/shouldShowUrl.svg",
shouldShowUrl: true,
},
},
},
},
],
},
{
type: "dynamic-suggestions",
suggestion_type: "isBlockable",
attachment: [
{
keywords: ["isblockable"],
dismissal_key: "isblockable-dismissal-key",
data: {
result: {
payload: {
title: "isBlockable",
url: "https://example.com/isBlockable",
icon: "https://example.com/isBlockable.svg",
isBlockable: true,
},
},
},
},
],
},
{
type: "dynamic-suggestions",
suggestion_type: "rowLabel",
attachment: [
{
keywords: ["rowlabel"],
data: {
result: {
rowLabel: {
id: "urlbar-group-search-suggestions",
args: { engine: "Test" },
},
payload: {
url: "https://example.com/rowLabel",
},
},
},
},
],
},
];
add_setup(async function () {
await QuickSuggestTestUtils.ensureQuickSuggestInit({
remoteSettingsRecords: REMOTE_SETTINGS_DATA,
});
await UrlbarTestUtils.initNimbusFeature({
quickSuggestDynamicSuggestionTypes:
"basic,shouldShowUrl,isBlockable,rowLabel",
});
// Wait until dynamic suggestion is available.
await BrowserTestUtils.waitForCondition(async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "basic",
});
const result = UrlbarTestUtils.getResultCount(window) == 2;
await UrlbarTestUtils.promisePopupClose(window);
return result;
});
});
add_task(async function basic() {
await BrowserTestUtils.withNewTab("about:blank", async () => {
info("Open urlbar with keyword");
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "basic",
});
Assert.equal(UrlbarTestUtils.getResultCount(window), 2);
info("Check the result");
const { element, result } = await UrlbarTestUtils.getDetailsOfResultAt(
window,
1
);
Assert.equal(result.providerName, UrlbarProviderQuickSuggest.name);
Assert.equal(result.payload.provider, "Dynamic");
assertUI(
element.row,
REMOTE_SETTINGS_DATA[0].attachment[0].data.result.payload
);
info("Activate this item");
const onLoad = BrowserTestUtils.browserLoaded(
gBrowser.selectedBrowser,
false,
result.payload.url
);
EventUtils.synthesizeMouseAtCenter(element.row, {});
await onLoad;
Assert.ok(true, "Expected page is loaded");
});
await PlacesUtils.history.clear();
});
add_task(async function basic_learn_more() {
info("Open urlbar with keyword");
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "basic",
});
info("Selecting Learn more item from the result menu");
let tabOpenPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
QuickSuggest.HELP_URL
);
await UrlbarTestUtils.openResultMenuAndClickItem(window, "help", {
resultIndex: 1,
});
await tabOpenPromise;
gBrowser.removeCurrentTab();
});
add_task(async function basic_manage() {
await doManageTest({ index: 1, input: "basic" });
});
add_task(async function shouldShowUrl() {
await BrowserTestUtils.withNewTab("about:blank", async () => {
info("Open urlbar with keyword");
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "shouldshowurl",
});
Assert.equal(UrlbarTestUtils.getResultCount(window), 2);
info("Check the result");
const { element, result } = await UrlbarTestUtils.getDetailsOfResultAt(
window,
1
);
Assert.equal(result.providerName, UrlbarProviderQuickSuggest.name);
Assert.equal(result.payload.provider, "Dynamic");
assertUI(
element.row,
REMOTE_SETTINGS_DATA[1].attachment[0].data.result.payload
);
});
});
add_task(async function isBlockable() {
await BrowserTestUtils.withNewTab("about:blank", async () => {
info("Open urlbar with keyword");
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "isblockable",
});
Assert.equal(UrlbarTestUtils.getResultCount(window), 2);
info("Check the result");
const { result } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
Assert.equal(result.providerName, UrlbarProviderQuickSuggest.name);
Assert.equal(result.payload.provider, "Dynamic");
info("Dismiss this item");
let dismissalPromise = TestUtils.topicObserved(
"quicksuggest-dismissals-changed"
);
await UrlbarTestUtils.openResultMenuAndClickItem(window, "dismiss", {
resultIndex: 1,
});
await dismissalPromise;
Assert.ok(
UrlbarTestUtils.isPopupOpen(window),
"View remains open after blocking result"
);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
"Result should be dismissed"
);
await UrlbarTestUtils.promisePopupClose(window);
await QuickSuggest.clearDismissedSuggestions();
});
});
add_task(async function rowLabel() {
info("Open urlbar with keyword");
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "rowlabel",
});
Assert.equal(UrlbarTestUtils.getResultCount(window), 2);
info("Check the row label");
const { element } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
Assert.equal(element.row.getAttribute("label"), "Test suggestions");
});
function assertUI(row, payload) {
const titleElement = row.querySelector(".urlbarView-title");
Assert.equal(titleElement.textContent, payload.title);
const faviconElement = row.querySelector(".urlbarView-favicon");
Assert.equal(faviconElement.src, payload.icon);
const urlElement = row.querySelector(".urlbarView-url");
const displayUrl = payload.shouldShowUrl
? payload.url.replace(/^https:\/\//, "")
: "";
Assert.equal(urlElement.textContent, displayUrl);
}

View File

@@ -40,6 +40,7 @@
--urlbarView-favicon-margin-end: var(--urlbarView-icon-margin-end);
--urlbarView-rich-suggestion-default-icon-size: 28px;
--urlbarView-top-pick-large-icon-box-size: 52px;
--urlbarView-result-button-size: 24px;
--urlbarView-result-button-background-opacity: 60%;
@@ -858,6 +859,12 @@
flex-basis: 24px;
}
&[icon-size="32"] {
width: 32px;
height: 32px;
flex-basis: 32px;
}
&[icon-size="38"] {
width: 38px;
height: 38px;
@@ -938,7 +945,7 @@
}
.urlbarView-row:is([type$=_amo], [type$=_pocket])[icon-size="24"] > .urlbarView-row-inner > .urlbarView-favicon {
padding: calc((52px - 24px) / 2);
padding: calc((var(--urlbarView-top-pick-large-icon-box-size) - 24px) / 2);
background-color: var(--urlbar-box-focus-bgcolor);
border-radius: 2px;
}
@@ -947,6 +954,10 @@
background-color: var(--urlbarView-result-button-selected-background-color);
}
.urlbarView-row[type$=_vpn][icon-size="32"] > .urlbarView-row-inner > .urlbarView-favicon {
padding: calc((var(--urlbarView-top-pick-large-icon-box-size) - 32px) / 2);
}
.urlbarView-row[dynamicType=weather],
.urlbarView-row[type=weather] {
/* Use the colors in the icon SVG files except in HCM and when the row is