icecat: add release icecat-140.6.0-1gnu1 for ecne

This commit is contained in:
Ark74 2026-01-17 19:26:27 -06:00
parent 618c9f4145
commit 7d0f5dab3b
3382 changed files with 457689 additions and 569094 deletions

View file

@ -969,13 +969,6 @@ export default class LoginItem extends HTMLElement {
}
_updatePasswordRevealState() {
if (
window.AboutLoginsUtils &&
window.AboutLoginsUtils.passwordRevealVisible === false
) {
this._revealCheckbox.hidden = true;
}
let { checked } = this._revealCheckbox;
let inputType = checked ? "text" : "password";
this._passwordInput.type = inputType;
@ -988,6 +981,13 @@ export default class LoginItem extends HTMLElement {
this._revealCheckbox.hidden = false;
}
if (
window.AboutLoginsUtils &&
window.AboutLoginsUtils.passwordRevealVisible === false
) {
this._revealCheckbox.hidden = true;
}
// Swap which <input> is in the document depending on whether we need the
// real .value (which means that the primary password was already entered,
// if applicable)

View file

@ -1576,6 +1576,27 @@ export var Policies = {
},
},
GenerativeAI: {
onBeforeAddons(manager, param) {
const defaultValue = "Enabled" in param ? param.Enabled : undefined;
const features = [
["Chatbot", ["browser.ml.chat.enabled", "browser.ml.chat.page"]],
["LinkPreviews", ["browser.ml.linkPreview.optin"]],
["TabGroups", ["browser.tabs.groups.smart.userEnabled"]],
];
for (const [key, prefs] of features) {
const value = key in param ? param[key] : defaultValue;
if (value !== undefined) {
for (const pref of prefs) {
PoliciesUtils.setDefaultPref(pref, value, param.Locked);
}
}
}
},
},
GoToIntranetSiteForSingleWordEntryInAddressBar: {
onBeforeAddons(manager, param) {
setAndLockPref("browser.fixup.dns_first_for_single_words", param);
@ -2080,6 +2101,7 @@ export var Policies = {
"security.tls.hello_downgrade_check",
"security.tls.version.enable-deprecated",
"security.warn_submit_secure_to_insecure",
"security.webauthn.always_allow_direct_attestation",
];
const blockedPrefs = [
"app.update.channel",

View file

@ -850,6 +850,27 @@
}
},
"GenerativeAI": {
"type": "object",
"properties": {
"Chatbot": {
"type": "boolean"
},
"LinkPreviews": {
"type": "boolean"
},
"TabGroups": {
"type": "boolean"
},
"Enabled": {
"type": "boolean"
},
"Locked": {
"type": "boolean"
}
}
},
"GoToIntranetSiteForSingleWordEntryInAddressBar": {
"type": "boolean"
},

View file

@ -4,6 +4,41 @@
"use strict";
add_task(async function test_hidden_reveal_password() {
let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(
Ci.nsILoginInfo
);
login.init("https://example.com", "", null, "username", "password");
login.QueryInterface(Ci.nsILoginMetaInfo);
login.timePasswordChanged = Date.now();
await Services.logins.addLoginAsync(login);
await setupPolicyEngineWithJson({
policies: {
DisablePasswordReveal: true,
},
});
let aboutLoginsTab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
url: "about:logins",
});
let browser = gBrowser.selectedBrowser;
await SpecialPowers.spawn(browser, [], () => {
let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
let passwordReveal = loginItem.shadowRoot.querySelector(
".reveal-password-checkbox"
);
is(passwordReveal.hidden, true, "Password reveal button should be hidden");
});
BrowserTestUtils.removeTab(aboutLoginsTab);
await Services.logins.removeAllLogins();
});
add_task(async function test_bug_1696948() {
await setupPolicyEngineWithJson({
policies: {
DisablePasswordReveal: true,

View file

@ -1184,6 +1184,38 @@ const POLICIES_TESTS = [
// browser/components/enterprisepolicies/tests/browser/browser_policy_usermessaging.js
},
},
// Bug 1981587
{
policies: {
Preferences: {
"security.webauthn.always_allow_direct_attestation": {
Value: true,
Status: "locked",
},
},
},
lockedPrefs: {
"security.webauthn.always_allow_direct_attestation": true,
},
},
// GenerativeAI
{
policies: {
GenerativeAI: {
Enabled: false,
Chatbot: true,
Locked: true,
},
},
lockedPrefs: {
"browser.ml.chat.enabled": true,
"browser.ml.chat.page": true,
"browser.ml.linkPreview.optin": false,
"browser.tabs.groups.smart.userEnabled": false,
},
},
];
add_task(async function test_policy_simple_prefs() {

View file

@ -166,7 +166,7 @@ export const LinkPreview = {
},
get canShowKeyPoints() {
return this._isRegionSupported();
return this._isRegionSupported() && !this._isDisabledByPolicy();
},
get canShowLegacy() {
@ -751,6 +751,17 @@ export const LinkPreview = {
return !disallowedRegions.includes(userRegion);
},
/**
* Checks if key points generation is disabled by policy.
*
* @returns {boolean} True if disabled by policy, false otherwise.
*/
_isDisabledByPolicy() {
return (
!lazy.optin && Services.prefs.prefIsLocked("browser.ml.linkPreview.optin")
);
},
/**
* Creates an Open Graph (OG) card using meta information from the page.
*

View file

@ -419,6 +419,13 @@ class LinkPreviewCard extends MozLitElement {
return "";
}
if (
!this.optin &&
Services.prefs.prefIsLocked("browser.ml.linkPreview.optin")
) {
return "";
}
// Determine if there's any generation error state
const isGenerationError =
this.isMissingDataErrorState || this.generationError;

View file

@ -450,3 +450,54 @@ add_task(async function test_no_optin_or_keypoints_in_disallowed_region() {
panel.remove();
generateStub.restore();
});
/**
* Test when the preference is false and locked, the opt-in prompt will not
* be shown and no attempt will be made to generate key points.
*/
add_task(async function test_locked_preference() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.linkPreview.enabled", true],
["browser.ml.linkPreview.optin", false],
["browser.ml.linkPreview.collapsed", false],
],
});
Services.prefs.lockPref("browser.ml.linkPreview.optin");
const generateStub = sinon.stub(LinkPreviewModel, "generateTextAI");
LinkPreview.keyboardComboActive = true;
XULBrowserWindow.setOverLink(
"https://example.com/browser/browser/components/genai/tests/browser/data/readableEn.html",
{}
);
let panel = await TestUtils.waitForCondition(() =>
document.getElementById("link-preview-panel")
);
await BrowserTestUtils.waitForEvent(panel, "popupshown");
const card = panel.querySelector("link-preview-card");
ok(card, "card created for link preview");
ok(
!LinkPreview.canShowKeyPoints,
"LinkPreview should indicate key points cannot be shown"
);
// Verify that the opt-in element is NOT present
const modelOptinElement = card.shadowRoot.querySelector("model-optin");
ok(!modelOptinElement, "model-optin element should NOT be present");
is(
generateStub.callCount,
0,
"generateTextAI should not be called in a disallowed region"
);
panel.remove();
generateStub.restore();
Services.prefs.unlockPref("browser.ml.linkPreview.optin");
});

View file

@ -214,7 +214,8 @@ class HistoryInView extends ViewPage {
return (
this.profileAge < 8 &&
!this.hasImportedHistoryPref &&
!this.importHistoryDismissedPref
!this.importHistoryDismissedPref &&
Services.policies.isAllowed("profileImport")
);
}

View file

@ -49,7 +49,7 @@
#include placesContextMenu.inc.xhtml
#include bookmarksHistoryTooltip.inc.xhtml
<box id="sidebar-panel-header" align="center">
<h4 data-l10n-id="sidebar-menu-bookmarks-2"></h4>
<html:h4 data-l10n-id="sidebar-menu-bookmarks-2"></html:h4>
<html:moz-button
id="sidebar-panel-close"
type="icon ghost"

View file

@ -797,6 +797,15 @@ class PlacesViewBase {
}
if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
if (this.#isPopupForRecursiveFolderShortcut(popup)) {
// Show as an empty container for now. We may want to show a better
// message in the future, but since we are likely to remove recursive
// shortcuts in maintenance at a certain point, this should be enough.
this._setEmptyPopupStatus(popup, true);
popup._built = true;
return;
}
if (!popup._placesNode.containerOpen) {
popup._placesNode.containerOpen = true;
}
@ -819,6 +828,33 @@ class PlacesViewBase {
aObject.removeEventListener(aEventNames[i], this, aCapturing);
}
}
/**
* Walks up the parent chain to detect whether a folder shortcut resolves to
* a folder already present in the ancestry.
*
* @param {DOMElement} popup
* @returns {boolean} Whether this popup is for a recursive folder shortcut.
*/
#isPopupForRecursiveFolderShortcut(popup) {
if (
!popup._placesNode ||
!PlacesUtils.nodeIsFolderOrShortcut(popup._placesNode)
) {
return false;
}
let guid = PlacesUtils.getConcreteItemGuid(popup._placesNode);
for (
let parentView = popup.parentNode?.parentNode;
parentView?._placesNode;
parentView = parentView.parentNode?.parentNode
) {
if (PlacesUtils.getConcreteItemGuid(parentView._placesNode) == guid) {
return true;
}
}
return false;
}
}
/**

View file

@ -214,6 +214,8 @@ skip-if = ["verify && debug && os == 'win'"]
["browser_paste_resets_cut_highlights.js"]
["browser_recursive_hierarchies.js"]
["browser_remove_bookmarks.js"]
["browser_sidebar_bookmarks_telemetry.js"]

View file

@ -0,0 +1,160 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Verify that menu popups are not populated when doing so would create a
* recursive hierarchy. Some consumers may walk menu trees and could enter an
* infinite loop if recursion were allowed.
*/
"use strict";
async function openMenuAndWaitForPopup(menu) {
let popup = menu.menupopup;
let popupShown = BrowserTestUtils.waitForEvent(popup, "popupshown");
// This seems necessary, at least for the toolbar.
EventUtils.synthesizeNativeMouseEvent({
type: "mousemove",
target: menu,
atCenter: true,
});
menu.open = true;
await popupShown;
return popup;
}
function findNode(guid, view) {
for (let node of view.childNodes) {
console.log("visiting node", node, node._placesNode?.bookmarkGuid);
if (node._placesNode?.bookmarkGuid == guid) {
return node;
}
}
return null;
}
function fakeOpenMenu(popup) {
popup.dispatchEvent(new MouseEvent("popupshowing", { bubbles: true }));
popup.dispatchEvent(new MouseEvent("popupshown", { bubbles: true }));
}
function findPlacesNode(guid, container) {
for (let i = 0; i < container.childCount; i++) {
let node = container.getChild(i);
if (node.bookmarkGuid == guid) {
return node;
}
}
return null;
}
add_task(async function test() {
// Make sure the bookmarks bar is visible and restore its state on cleanup.
let toolbar = document.getElementById("PersonalToolbar");
ok(toolbar, "PersonalToolbar should not be null");
if (toolbar.collapsed) {
await promiseSetToolbarVisibility(toolbar, true);
registerCleanupFunction(function () {
return promiseSetToolbarVisibility(toolbar, false);
});
}
let menubar = document.getElementById("toolbar-menubar");
// Force the menu to be shown.
const kAutohide = menubar.getAttribute("autohide");
menubar.removeAttribute("autohide");
registerCleanupFunction(function () {
menubar.setAttribute("autohide", kAutohide);
});
registerCleanupFunction(PlacesUtils.bookmarks.eraseEverything);
// Create a folder structure with a recursive shortcut.
let folderA = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
title: "Folder A",
type: PlacesUtils.bookmarks.TYPE_FOLDER,
});
let selfShortcut = await PlacesUtils.bookmarks.insert({
parentGuid: folderA.guid,
title: "Shortcut to A",
url: `place:parent=${folderA.guid}`,
});
let toolbarShortcut = await PlacesUtils.bookmarks.insert({
parentGuid: folderA.guid,
title: "Shortcut to toolbar",
url: `place:parent=${PlacesUtils.bookmarks.toolbarGuid}`,
});
// This also ensures that toolbar content is updated.
Assert.ok(!(await PlacesToolbarHelper.getIsEmpty()), "Toolbar is not empty");
// Open the popup for the shortcut from the bookmarks toolbar.
let toolbarItems = document.getElementById("PlacesToolbarItems");
let folderANode = findNode(folderA.guid, toolbarItems);
let folderAPopup = await openMenuAndWaitForPopup(folderANode);
let selfShortcutNode = findNode(selfShortcut.guid, folderAPopup);
let selfShortcutPopup = await openMenuAndWaitForPopup(selfShortcutNode);
Assert.ok(
!selfShortcutPopup._placesNode.containerOpen,
"Self shortcut container is not open"
);
Assert.ok(
selfShortcutPopup.hasAttribute("emptyplacesresult"),
"Self shortcut popup is empty"
);
let toolbarShortcutNode = findNode(
toolbarShortcut.guid,
folderANode.menupopup
);
let toolbarShortcutPopup = await openMenuAndWaitForPopup(toolbarShortcutNode);
Assert.ok(
!toolbarShortcutPopup._placesNode.containerOpen,
"Toolbar shortcut container is not open"
);
Assert.ok(
toolbarShortcutPopup.hasAttribute("emptyplacesresult"),
"Toolbar shortcut popup is empty"
);
// Also insert a toolbar shortcut in the bookmarks menu and check the
// previously inserted recursive toolbar shortcut there.
info("Test native bookmarks menu");
let shortcutInMenu = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.menuGuid,
title: "Shortcut to menu",
url: `place:parent=${PlacesUtils.bookmarks.menuGuid}`,
});
if (Services.appinfo.nativeMenubar) {
// With native menubar we can only simulate popupshowing as we cannot
// directly open menus.
let bookmarksMenuPopup = document.getElementById("bookmarksMenuPopup");
fakeOpenMenu(bookmarksMenuPopup);
let shortcutInMenuNode = findNode(shortcutInMenu.guid, bookmarksMenuPopup);
fakeOpenMenu(shortcutInMenuNode.menupopup);
Assert.ok(
!shortcutInMenuNode.menupopup._placesNode.containerOpen,
"menu shortcut container is not open"
);
} else {
let bookmarksMenu = document.getElementById("bookmarksMenu");
let bookmarksPopup = await openMenuAndWaitForPopup(bookmarksMenu);
let shortcutInMenuNode = findNode(shortcutInMenu.guid, bookmarksPopup);
let shortcutInMenuPopup = await openMenuAndWaitForPopup(
shortcutInMenuNode,
true
);
Assert.ok(
!shortcutInMenuPopup._placesNode.containerOpen,
"Toolbar shortcut container is not open"
);
Assert.ok(
shortcutInMenuPopup.hasAttribute("emptyplacesresult"),
"Toolbar shortcut popup is empty"
);
}
});

View file

@ -3,10 +3,14 @@
const PATH_NET = TEST_PATH + "file_dummy.html";
const PATH_ORG = PATH_NET.replace("example.net", "example.org");
add_task(async function () {
async function runTest(defaultZoom) {
let tab1, tab1Zoom;
tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PATH_NET);
tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
is(tab1Zoom, defaultZoom, "We are starting with the default zoom.");
await FullZoom.setZoom(1.25, tab1.linkedBrowser);
tab1Zoom = ZoomManager.getZoomForBrowser(tab1.linkedBrowser);
@ -46,7 +50,7 @@ add_task(async function () {
is(
tab1Zoom,
1.0,
defaultZoom,
"privacy.resistFingerprinting is true, site-specific zoom should be reset when clearing FPP state for tab1"
);
@ -55,4 +59,29 @@ add_task(async function () {
BrowserTestUtils.removeTab(tab1);
await SpecialPowers.popPrefEnv();
}
add_task(async function () {
await runTest(1.0);
let defaultZoom = 1.5;
let context = Cu.createLoadContext();
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
let { promise, resolve, reject } = Promise.withResolvers();
cps2.setGlobal(FullZoom.name, defaultZoom, context, {
handleError(error) {
reject(error);
},
handleCompletion() {
resolve();
},
});
await promise;
try {
await runTest(defaultZoom);
} finally {
cps2.removeGlobal(FullZoom.name, context);
}
});

View file

@ -16,6 +16,7 @@ import { SidebarPage } from "./sidebar-page.mjs";
ChromeUtils.defineESModuleGetters(lazy, {
HistoryController: "resource:///modules/HistoryController.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
});
@ -72,6 +73,10 @@ export class SidebarHistory extends SidebarPage {
if (!this.triggerNode) {
e.preventDefault();
}
let privateWindowMenuItem = this._contextMenu.querySelector(
"#sidebar-history-context-open-in-private-window"
);
privateWindowMenuItem.hidden = !lazy.PrivateBrowsingUtils.enabled;
}
handleCommandEvent(e) {

View file

@ -4,6 +4,7 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
SyncedTabsController: "resource:///modules/SyncedTabsController.sys.mjs",
});
@ -75,6 +76,11 @@ class SyncedTabsInSidebar extends SidebarPage {
);
// Enable the feature only if the device supports it
closeTabMenuItem.disabled = !this.triggerNode.canClose;
let privateWindowMenuItem = contextMenu.querySelector(
"#sidebar-synced-tabs-context-open-in-private-window"
);
privateWindowMenuItem.hidden = !lazy.PrivateBrowsingUtils.enabled;
}
handleCommandEvent(e) {

View file

@ -159,6 +159,9 @@ const DEFAULT_ACTIONS = {
l10nCommands: ["quickactions-cmd-print"],
label: "quickactions-print2",
icon: "chrome://global/skin/icons/print.svg",
isVisible: () => {
return Services.prefs.getBoolPref("print.enabled");
},
onPick: () => {
lazy.BrowserWindowTracker.getTopWindow()
.document.getElementById("cmd_print")
@ -193,6 +196,9 @@ const DEFAULT_ACTIONS = {
l10nCommands: ["quickactions-cmd-savepdf2"],
label: "quickactions-savepdf",
icon: "chrome://global/skin/icons/print.svg",
isVisible: () => {
return Services.prefs.getBoolPref("print.enabled");
},
onPick: () => {
// This writes over the users last used printer which we
// should not do. Refactor to launch the print preview with

View file

@ -82,11 +82,6 @@ export class AddonSuggestions extends SuggestProvider {
return null;
}
if (suggestion.source == "rust") {
suggestion.icon = suggestion.iconUrl;
delete suggestion.iconUrl;
}
// Set UTM params unless they're already defined. This allows remote
// settings or Merino to override them if need be.
let url = new URL(suggestion.url);
@ -100,6 +95,8 @@ export class AddonSuggestions extends SuggestProvider {
url: url.href,
originalUrl: suggestion.url,
shouldShowUrl: true,
// Rust uses `iconUrl` but Merino uses `icon`.
icon: suggestion.iconUrl ?? suggestion.icon,
title: suggestion.title,
description: suggestion.description,
bottomTextL10n: { id: "icecat-suggest-addons-recommended" },

View file

@ -943,8 +943,10 @@ class _QuickSuggestTestUtils {
source = "rust",
provider = "Yelp",
isTopPick = false,
// The default Yelp suggestedIndex is 0, unlike most other Suggest
// suggestion types, which use -1.
// The logic for the default Yelp `suggestedIndex` is complex and depends on
// whether `UrlbarProviderSearchSuggestions` is active and whether search
// suggestions are shown first. By default -- when the answer to both of
// those questions is Yes -- Yelp's `suggestedIndex` is 0.
suggestedIndex = 0,
isSuggestedIndexRelativeToGroup = true,
originalUrl = undefined,

View file

@ -263,10 +263,16 @@ async function doDismissOneTest({
"quicksuggest-dismissals-changed"
);
let actualResult = await getActualResult({
providers: [UrlbarProviderQuickSuggest.name],
query: queriesForDismissals[0].query,
expectedResult: result,
});
triggerCommand({
result,
command,
feature,
result: actualResult,
expectedCountsByCall: {
removeResult: 1,
},
@ -280,7 +286,7 @@ async function doDismissOneTest({
"canClearDismissedSuggestions should return true after triggering command"
);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
await QuickSuggest.isResultDismissed(actualResult),
"The result should be dismissed"
);
@ -374,10 +380,16 @@ async function doDismissAllTest({ feature, result, command, pref, queries }) {
"quicksuggest-dismissals-changed"
);
let actualResult = await getActualResult({
providers: [UrlbarProviderQuickSuggest.name],
query: queries[0].query,
expectedResult: result,
});
triggerCommand({
result,
command,
feature,
result: actualResult,
expectedCountsByCall: {
removeResult: 1,
},
@ -442,6 +454,44 @@ async function doDismissAllTest({ feature, result, command, pref, queries }) {
}
}
/**
* Does a search, asserts an actual result exists that matches the given result,
* and returns it.
*
* @param {object} options
* Options object.
* @param {SuggestFeature} options.query
* The search string.
* @param {UrlbarResult} options.expectedResult
* The expected result.
* @param {string[]} [options.providers]
* The providers to query.
*/
async function getActualResult({
query,
expectedResult,
providers = [UrlbarProviderQuickSuggest.name],
}) {
info("Doing search to get an actual result: " + JSON.stringify(query));
let context = createContext(query, {
providers,
isPrivate: false,
});
await check_results({
context,
matches: [expectedResult],
});
let actualResult = context.results.find(
r =>
r.providerName == UrlbarProviderQuickSuggest.name &&
r.payload.provider == expectedResult.payload.provider
);
Assert.ok(actualResult, "Search should have returned a matching result");
return actualResult;
}
/**
* Does some "show less frequently" tests where the cap is set in remote
* settings and Nimbus. See `doOneShowLessFrequentlyTest()`. This function