diff --git a/firefox-src-part/browser/actors/ClickHandlerParent.sys.mjs b/firefox-src-part/browser/actors/ClickHandlerParent.sys.mjs index bdb722d95..bd3112f1a 100644 --- a/firefox-src-part/browser/actors/ClickHandlerParent.sys.mjs +++ b/firefox-src-part/browser/actors/ClickHandlerParent.sys.mjs @@ -119,6 +119,7 @@ export class ClickHandlerParent extends JSWindowActorParent { openerBrowser: browser, // The child ensures that untrusted events have a valid user activation. hasValidUserGestureActivation: true, + textDirectiveUserActivation: true, triggeringRemoteType: this.manager.domProcess?.remoteType, }; diff --git a/firefox-src-part/browser/actors/ContextMenuChild.sys.mjs b/firefox-src-part/browser/actors/ContextMenuChild.sys.mjs index 2b98bea65..8c1824f2b 100644 --- a/firefox-src-part/browser/actors/ContextMenuChild.sys.mjs +++ b/firefox-src-part/browser/actors/ContextMenuChild.sys.mjs @@ -113,13 +113,6 @@ export class ContextMenuChild extends JSWindowActorChild { } break; case "pictureinpicture": - if (!media.isCloningElementVisually) { - Services.telemetry.keyedScalarAdd( - "pictureinpicture.opened_method", - "contextmenu", - 1 - ); - } let event = new this.contentWindow.CustomEvent( "MozTogglePictureInPicture", { diff --git a/firefox-src-part/browser/actors/ContextMenuParent.sys.mjs b/firefox-src-part/browser/actors/ContextMenuParent.sys.mjs index 4c67bc75e..a14da27f6 100644 --- a/firefox-src-part/browser/actors/ContextMenuParent.sys.mjs +++ b/firefox-src-part/browser/actors/ContextMenuParent.sys.mjs @@ -3,10 +3,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs", FirefoxRelay: "resource://gre/modules/FirefoxRelay.sys.mjs", + WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.sys.mjs", +}); + +XPCOMUtils.defineLazyServiceGetters(lazy, { + BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"], }); export class ContextMenuParent extends JSWindowActorParent { @@ -18,14 +26,14 @@ export class ContextMenuParent extends JSWindowActorParent { // loads nsContextMenu.js. In that case, try to find the chromeEventHandler, // since that'll likely be the "top" , and then use its window's // nsContextMenu instance instead. - if (!win.openContextMenu) { + if (!win.nsContextMenu) { let topBrowser = browser.ownerGlobal.docShell.chromeEventHandler; win = topBrowser.ownerGlobal; } message.data.context.showRelay &&= lazy.FirefoxRelay.isEnabled; - win.openContextMenu(message, browser, this); + this.#openContextMenu(message.data, win, browser); } hiding() { @@ -114,4 +122,113 @@ export class ContextMenuParent extends JSWindowActorParent { targetIdentifier, }); } + + /** + * Handles opening of the context menu for the appropraite browser. + * + * @param {object} data + * The data for the context menu, received from the child. + * @param {DOMWindow} win + * The window in which the context menu is to be opened. + * @param {Browser} browser + * The browser the context menu is being opened for. + */ + #openContextMenu(data, win, browser) { + if (lazy.BrowserHandler.kiosk) { + // Don't display context menus in kiosk mode + return; + } + let wgp = this.manager; + + if (!wgp.isCurrentGlobal) { + // Don't display context menus for unloaded documents + return; + } + + // NOTE: We don't use `wgp.documentURI` here as we want to use the failed + // channel URI in the case we have loaded an error page. + let documentURIObject = wgp.browsingContext.currentURI; + + let frameReferrerInfo = data.frameReferrerInfo; + if (frameReferrerInfo) { + frameReferrerInfo = + lazy.E10SUtils.deserializeReferrerInfo(frameReferrerInfo); + } + + let linkReferrerInfo = data.linkReferrerInfo; + if (linkReferrerInfo) { + linkReferrerInfo = + lazy.E10SUtils.deserializeReferrerInfo(linkReferrerInfo); + } + + let frameID = lazy.WebNavigationFrames.getFrameId(wgp.browsingContext); + + win.nsContextMenu.contentData = { + context: data.context, + browser, + actor: this, + editFlags: data.editFlags, + spellInfo: data.spellInfo, + principal: wgp.documentPrincipal, + storagePrincipal: wgp.documentStoragePrincipal, + documentURIObject, + docLocation: documentURIObject.spec, + charSet: data.charSet, + referrerInfo: lazy.E10SUtils.deserializeReferrerInfo(data.referrerInfo), + frameReferrerInfo, + linkReferrerInfo, + contentType: data.contentType, + contentDisposition: data.contentDisposition, + frameID, + frameOuterWindowID: frameID, + frameBrowsingContext: wgp.browsingContext, + selectionInfo: data.selectionInfo, + disableSetDesktopBackground: data.disableSetDesktopBackground, + showRelay: data.showRelay, + loginFillInfo: data.loginFillInfo, + userContextId: wgp.browsingContext.originAttributes.userContextId, + webExtContextData: data.webExtContextData, + cookieJarSettings: wgp.cookieJarSettings, + }; + + let popup = win.document.getElementById("contentAreaContextMenu"); + let context = win.nsContextMenu.contentData.context; + + // Fill in some values in the context from the WindowGlobalParent actor. + context.principal = wgp.documentPrincipal; + context.storagePrincipal = wgp.documentStoragePrincipal; + context.frameID = frameID; + context.frameOuterWindowID = wgp.outerWindowId; + context.frameBrowsingContextID = wgp.browsingContext.id; + + // We don't have access to the original event here, as that happened in + // another process. Therefore we synthesize a new MouseEvent to propagate the + // inputSource to the subsequently triggered popupshowing event. + let newEvent = new PointerEvent("contextmenu", { + bubbles: true, + cancelable: true, + screenX: context.screenXDevPx / win.devicePixelRatio, + screenY: context.screenYDevPx / win.devicePixelRatio, + button: 2, + pointerType: (() => { + switch (context.inputSource) { + case MouseEvent.MOZ_SOURCE_MOUSE: + return "mouse"; + case MouseEvent.MOZ_SOURCE_PEN: + return "pen"; + case MouseEvent.MOZ_SOURCE_ERASER: + return "eraser"; + case MouseEvent.MOZ_SOURCE_CURSOR: + return "cursor"; + case MouseEvent.MOZ_SOURCE_TOUCH: + return "touch"; + case MouseEvent.MOZ_SOURCE_KEYBOARD: + return "keyboard"; + default: + return ""; + } + })(), + }); + popup.openPopupAtScreen(newEvent.screenX, newEvent.screenY, true, newEvent); + } } diff --git a/firefox-src-part/browser/actors/LinkHandlerParent.sys.mjs b/firefox-src-part/browser/actors/LinkHandlerParent.sys.mjs index 5610c7122..910276e6a 100644 --- a/firefox-src-part/browser/actors/LinkHandlerParent.sys.mjs +++ b/firefox-src-part/browser/actors/LinkHandlerParent.sys.mjs @@ -5,7 +5,7 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { - PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs", + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", }); let gTestListeners = new Set(); @@ -131,7 +131,7 @@ export class LinkHandlerParent extends JSWindowActorParent { console.error(ex); return; } - if (iconURI.scheme != "data") { + if (!iconURI.schemeIs("data")) { try { Services.scriptSecurityManager.checkLoadURIWithPrincipal( browser.contentPrincipal, @@ -144,13 +144,11 @@ export class LinkHandlerParent extends JSWindowActorParent { } if (canStoreIcon) { try { - lazy.PlacesUIUtils.loadFavicon( - browser, - Services.scriptSecurityManager.getSystemPrincipal(), + lazy.PlacesUtils.favicons.setFaviconForPage( Services.io.newURI(pageURL), Services.io.newURI(originalURL), - expiration, - iconURI + iconURI, + expiration && lazy.PlacesUtils.toPRTime(expiration) ); } catch (ex) { console.error(ex); diff --git a/firefox-src-part/browser/actors/SearchSERPTelemetryChild.sys.mjs b/firefox-src-part/browser/actors/SearchSERPTelemetryChild.sys.mjs index f94d29b72..1a1588857 100644 --- a/firefox-src-part/browser/actors/SearchSERPTelemetryChild.sys.mjs +++ b/firefox-src-part/browser/actors/SearchSERPTelemetryChild.sys.mjs @@ -8,6 +8,7 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { clearTimeout: "resource://gre/modules/Timer.sys.mjs", + SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs", setTimeout: "resource://gre/modules/Timer.sys.mjs", }); @@ -18,6 +19,13 @@ XPCOMUtils.defineLazyPreferenceGetter( false ); +ChromeUtils.defineLazyGetter(lazy, "logConsole", () => { + return console.createInstance({ + prefix: "SearchTelemetry", + maxLogLevel: lazy.SearchUtils.loggingEnabled ? "Debug" : "Warn", + }); +}); + export const CATEGORIZATION_SETTINGS = { MAX_DOMAINS_TO_CATEGORIZE: 10, }; @@ -546,7 +554,16 @@ class SearchAdImpression { #categorizeAnchors(anchors, origin) { for (let anchor of anchors) { if (this.#shouldInspectAnchor(anchor, origin)) { - let result = this.#findDataForAnchor(anchor); + let result; + try { + // We use a schema to ensure the values for each search provider + // aligns to what is expected, but tests don't enforce the schema + // and thus, can technically input faulty values. + result = this.#findDataForAnchor(anchor); + } catch (ex) { + lazy.logConsole.error("Could not find data for anchor:", ex); + continue; + } if (result) { this.#recordElementData(result.element, { type: result.type, @@ -555,7 +572,7 @@ class SearchAdImpression { childElements: result.childElements, }); } - if (result.relatedElements?.length) { + if (result?.relatedElements?.length) { // Bug 1880413: Deprecate related elements. // Bottom-up approach with related elements are only used for // non-link elements related to ads, like carousel arrows. @@ -735,10 +752,13 @@ class SearchAdImpression { * * @param {HTMLAnchorElement} anchor * The anchor to be inspected. - * @returns {object} + * @returns {object | null} * An object containing the element representing the root DOM element for * the component, the type of component, how many ads were counted, * and whether or not the count was of all the children. + * @throws {Error} + * Will throw an error if certain properties of a component are missing. + * Required properties are listed in search-telemetry-v2-schema.json. */ #findDataForAnchor(anchor) { for (let component of this.#providerInfo.components) { @@ -781,6 +801,12 @@ class SearchAdImpression { continue; } + // If a parent was found, we may want to ignore reporting the element + // to telemetry. + if (component.included.parent.skipCount) { + return null; + } + // If we've already inspected the parent, add the child element to the // list of anchors. Don't increment the ads loaded count, as we only care // about grouping the anchor with the correct parent. @@ -805,6 +831,9 @@ class SearchAdImpression { // If counting by child, get all of them at once. if (child.countChildren) { let proxyChildElements = parent.querySelectorAll(child.selector); + if (child.skipCount) { + return null; + } if (proxyChildElements.length) { return { element: parent, @@ -816,6 +845,9 @@ class SearchAdImpression { }; } } else if (parent.querySelector(child.selector)) { + if (child.skipCount) { + return null; + } return { element: parent, type: child.type ?? component.type, diff --git a/firefox-src-part/browser/actors/WebRTCParent.sys.mjs b/firefox-src-part/browser/actors/WebRTCParent.sys.mjs index ae9f4d0a8..be66de4a1 100644 --- a/firefox-src-part/browser/actors/WebRTCParent.sys.mjs +++ b/firefox-src-part/browser/actors/WebRTCParent.sys.mjs @@ -671,6 +671,15 @@ function prompt(aActor, aBrowser, aRequest) { ? lazy.SitePermissions.SCOPE_PERSISTENT : lazy.SitePermissions.SCOPE_TEMPORARY; if (reqAudioInput) { + if (!isPersistent) { + // After a temporary block, having permissions.query() calls + // persistently report "granted" would be misleading + maybeClearAlwaysAsk( + principal, + "microphone", + notification.browser + ); + } lazy.SitePermissions.setForPrincipal( principal, "microphone", @@ -680,6 +689,11 @@ function prompt(aActor, aBrowser, aRequest) { ); } if (reqVideoInput) { + if (!isPersistent && !sharingScreen) { + // After a temporary block, having permissions.query() calls + // persistently report "granted" would be misleading + maybeClearAlwaysAsk(principal, "camera", notification.browser); + } lazy.SitePermissions.setForPrincipal( principal, sharingScreen ? "screen" : "camera", @@ -1134,12 +1148,8 @@ function prompt(aActor, aBrowser, aRequest) { ({ deviceIndex }) => deviceIndex == videoDeviceIndex ); aActor.activateDevicePerm(aRequest.windowID, mediaSource, rawId); - if (remember) { - lazy.SitePermissions.setForPrincipal( - principal, - "camera", - lazy.SitePermissions.ALLOW - ); + if (!sharingScreen) { + persistGrantOrPromptPermission(principal, "camera", remember); } } } @@ -1155,13 +1165,7 @@ function prompt(aActor, aBrowser, aRequest) { ({ deviceIndex }) => deviceIndex == audioDeviceIndex ); aActor.activateDevicePerm(aRequest.windowID, mediaSource, rawId); - if (remember) { - lazy.SitePermissions.setForPrincipal( - principal, - "microphone", - lazy.SitePermissions.ALLOW - ); - } + persistGrantOrPromptPermission(principal, "microphone", remember); } } else if (reqAudioInput === "AudioCapture") { // Only one device possible for audio capture. @@ -1319,31 +1323,6 @@ function prompt(aActor, aBrowser, aRequest) { options ); notification.callID = aRequest.callID; - - let schemeHistogram = Services.telemetry.getKeyedHistogramById( - "PERMISSION_REQUEST_ORIGIN_SCHEME" - ); - let userInputHistogram = Services.telemetry.getKeyedHistogramById( - "PERMISSION_REQUEST_HANDLING_USER_INPUT" - ); - - let docURI = aRequest.documentURI; - let scheme = 0; - if (docURI.startsWith("https")) { - scheme = 2; - } else if (docURI.startsWith("http")) { - scheme = 1; - } - - for (let requestType of requestTypes) { - if (requestType == "AudioCapture") { - requestType = "Microphone"; - } - requestType = requestType.toLowerCase(); - - schemeHistogram.add(requestType, scheme); - userInputHistogram.add(requestType, aRequest.isHandlingUserInput); - } } /** @@ -1497,3 +1476,53 @@ function clearTemporaryGrants(browser, clearCamera, clearMicrophone) { lazy.SitePermissions.removeFromPrincipal(null, perm.id, browser) ); } + +/** + * Persist an ALLOW state if the remember option is true. + * Otherwise, persist PROMPT so that we can later tell the site + * that permission was granted once before. + * This makes Firefox seem much more like Chrome to sites that + * expect a one-off, persistent permission grant for cam/mic. + * + * @param principal - Principal to add permission to. + * @param {string} permissionName - name of permission. + * @param remember - whether the grant should be persisted. + */ +function persistGrantOrPromptPermission(principal, permissionName, remember) { + // There are cases like unsafe delegation where a prompt appears + // even in ALLOW state, so make sure to not overwrite it (there's + // no remember checkbox in those cases) + if ( + lazy.SitePermissions.getForPrincipal(principal, permissionName).state == + lazy.SitePermissions.ALLOW + ) { + return; + } + + lazy.SitePermissions.setForPrincipal( + principal, + permissionName, + remember ? lazy.SitePermissions.ALLOW : lazy.SitePermissions.PROMPT + ); +} + +/** + * Clears any persisted PROMPT (aka Always Ask) permission. + * @param principal - Principal to remove permission from. + * @param {string} permissionName - name of permission. + * @param browser - Browser element to clear permission for. + */ +function maybeClearAlwaysAsk(principal, permissionName, browser) { + // For the "Always Ask" user choice, only persisted PROMPT is used, + // so no need to scan through temporary permissions. + if ( + lazy.SitePermissions.getForPrincipal(principal, permissionName).state == + lazy.SitePermissions.PROMPT + ) { + lazy.SitePermissions.removeFromPrincipal( + principal, + permissionName, + browser + ); + } +} diff --git a/firefox-src-part/browser/app/profile/firefox.js b/firefox-src-part/browser/app/profile/firefox.js index a39a4f287..525d31c9c 100644 --- a/firefox-src-part/browser/app/profile/firefox.js +++ b/firefox-src-part/browser/app/profile/firefox.js @@ -522,6 +522,13 @@ pref("browser.urlbar.quicksuggest.impressionCaps.nonSponsoredEnabled", false); // caps. pref("browser.urlbar.quicksuggest.impressionCaps.sponsoredEnabled", false); +// When non-zero, this is the character-count threshold (inclusive) for showing +// AMP suggestions as top picks. If an AMP suggestion is triggered by a keyword +// at least this many characters long, it will be shown as a top pick. Full +// keywords will also show AMP suggestions as top picks even if they have fewer +// characters than this threshold. +pref("browser.urlbar.quicksuggest.ampTopPickCharThreshold", 0); + // Whether unit conversion is enabled. #ifdef NIGHTLY_BUILD pref("browser.urlbar.unitConversion.enabled", true); @@ -765,6 +772,10 @@ pref("browser.search.serpMetricsRecordedCounter", 0); // days pref("browser.search.widget.removeAfterDaysUnused", 120); +// The number of times the search function in the URL bar has been used, +// capped at 100. +pref("browser.search.totalSearches", 0); + // Enable new experimental shopping features. This is solely intended as a // rollout/"emergency stop" button - it will go away once the feature has // rolled out. There will be separate controls for user opt-in/opt-out. @@ -866,6 +877,20 @@ pref("permissions.desktop-notification.notNow.enabled", false); pref("permissions.fullscreen.allowed", false); +#ifdef MOZ_WEBRTC + // When users grant camera or microphone through a permission prompt + // and leave "☐ Remember this decision" unchecked, Gecko persists + // their choice to "Always ask" for this permission going forward. + // This is exposed to websites through the permissions.query() API + // as "granted", to ward off well-meaning attempts to further escalate + // permission to always grant, to help sites respect this user choice. + // + // By default, these permissions are only visible in Tools / Page Info. + // But turning this pref on also makes them show up as "Always Ask ✖" + // in the more prominent site permissions dropdown. + pref("permissions.media.show_always_ask.enabled", false); +#endif + // Force external link opens into the default user context ID instead of guessing // the most appropriate one based on the URL (https://bugzilla.mozilla.org/show_bug.cgi?id=1874599#c8) pref("browser.link.force_default_user_context_id_for_external_opens", false); @@ -958,11 +983,7 @@ pref("browser.tabs.tooltipsShowPidAndActiveness", true); pref("browser.tabs.tooltipsShowPidAndActiveness", false); #endif -#ifdef NIGHTLY_BUILD pref("browser.tabs.hoverPreview.enabled", true); -#else -pref("browser.tabs.hoverPreview.enabled", false); -#endif pref("browser.tabs.hoverPreview.showThumbnails", true); pref("browser.tabs.firefox-view.logLevel", "Warn"); @@ -1454,7 +1475,7 @@ pref("browser.bookmarks.editDialog.maxRecentFolders", 7); // See - security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp // SetSecurityLevelForContentProcess() for what the different settings mean. #if defined(NIGHTLY_BUILD) - pref("security.sandbox.content.level", 7); + pref("security.sandbox.content.level", 8); #else pref("security.sandbox.content.level", 7); #endif @@ -1624,7 +1645,7 @@ pref("services.sync.prefs.sync.extensions.activeThemeID", true); pref("services.sync.prefs.sync.general.autoScroll", true); // general.autoScroll has a different default on Linux vs elsewhere. pref("services.sync.prefs.sync-seen.general.autoScroll", false); -pref("services.sync.prefs.sync.general.smoothScroll", true); +// general.smoothScroll is not synced, bug 1851024 pref("services.sync.prefs.sync.intl.accept_languages", true); pref("services.sync.prefs.sync.intl.regional_prefs.use_os_locales", true); pref("services.sync.prefs.sync.layout.spellcheckDefault", true); @@ -1750,6 +1771,10 @@ pref("browser.newtabpage.activity-stream.newtabWallpapers.highlightCtaText", "") pref("browser.newtabpage.activity-stream.newNewtabExperience.colors", "#0090ED,#FF4F5F,#2AC3A2,#FF7139,#A172FF,#FFA437,#FF2A8A"); +// Default layout experimentation +pref("browser.newtabpage.activity-stream.newtabLayouts.variant-a", false); +pref("browser.newtabpage.activity-stream.newtabLayouts.variant-b", false); + // Activity Stream prefs that control to which page to redirect #ifndef RELEASE_OR_BETA pref("browser.newtabpage.activity-stream.debug", false); @@ -1840,16 +1865,17 @@ pref("browser.newtabpage.activity-stream.discoverystream.region-bff-config", "US // List of regions that get topics selection by default. pref("browser.newtabpage.activity-stream.discoverystream.topicSelection.region-topics-config", "US, CA"); pref("browser.newtabpage.activity-stream.discoverystream.topicSelection.onboarding.enabled", true); + pref("browser.newtabpage.activity-stream.discoverystream.topicLabels.region-topic-label-config", "US, CA"); #else pref("browser.newtabpage.activity-stream.discoverystream.merino-provider.enabled", false); pref("browser.newtabpage.activity-stream.discoverystream.topicSelection.region-topics-config", ""); pref("browser.newtabpage.activity-stream.discoverystream.topicSelection.onboarding.enabled", false); + pref("browser.newtabpage.activity-stream.discoverystream.topicLabels.region-topic-label-config", ""); #endif // List of locales that get topics selection by default. pref("browser.newtabpage.activity-stream.discoverystream.topicSelection.locale-topics-config", "en-US, en-GB, en-CA"); -// System pref to enable topic labels on Pocket cards -pref("browser.newtabpage.activity-stream.discoverystream.topicLabels.enabled", true); +pref("browser.newtabpage.activity-stream.discoverystream.topicLabels.locale-topic-label-config", "en-US, en-GB, en-CA"); pref("browser.newtabpage.activity-stream.discoverystream.merino-provider.endpoint", "merino.services.mozilla.com"); // List of regions that get spocs by default. @@ -2051,7 +2077,7 @@ pref("identity.fxaccounts.commands.remoteTabManagement.enabled", true); // Controls whether or not the client association ping has values set on it // when the sync-ui-state:update notification fires. -pref("identity.fxaccounts.telemetry.clientAssociationPing.enabled", false); +pref("identity.fxaccounts.telemetry.clientAssociationPing.enabled", true); // Note: when media.gmp-*.visible is true, provided we're running on a // supported platform/OS version, the corresponding CDM appears in the @@ -3166,7 +3192,7 @@ pref("browser.backup.preferences.ui.enabled", false); pref("browser.backup.sqlite.pages_per_step", 50); // The delay between SQLite database backup steps in milliseconds. pref("browser.backup.sqlite.step_delay_ms", 50); -pref("browser.backup.scheduled.idle-threshold-seconds", 300); +pref("browser.backup.scheduled.idle-threshold-seconds", 15); pref("browser.backup.scheduled.minimum-time-between-backups-seconds", 3600); pref("browser.backup.template.fallback-download.release", "https://www.mozilla.org/firefox/download/thanks/?s=direct&utm_medium=firefox-desktop&utm_source=backup&utm_campaign=firefox-backup-2024&utm_content=control"); pref("browser.backup.template.fallback-download.beta", "https://www.mozilla.org/firefox/channel/desktop/?utm_medium=firefox-desktop&utm_source=backup&utm_campaign=firefox-backup-2024&utm_content=control#beta"); diff --git a/firefox-src-part/browser/base/content/aboutDialog.js b/firefox-src-part/browser/base/content/aboutDialog.js index fc0252ad1..8c5c7768e 100644 --- a/firefox-src-part/browser/base/content/aboutDialog.js +++ b/firefox-src-part/browser/base/content/aboutDialog.js @@ -43,15 +43,28 @@ function init() { } // Include the build ID and display warning if this is an "a#" (nightly or aurora) build - let versionId = "aboutDialog-version"; + let versionIdMap = new Map([ + ["base", "aboutDialog-version"], + ["base-nightly", "aboutDialog-version-nightly"], + ["base-arch", "aboutdialog-version-arch"], + ["base-arch-nightly", "aboutdialog-version-arch-nightly"], + ]); + let versionIdKey = "base"; let versionAttributes = { version: AppConstants.MOZ_APP_VERSION_DISPLAY, - bits: Services.appinfo.is64Bit ? 64 : 32, }; + let arch = Services.sysinfo.get("arch"); + if (["x86", "x86-64"].includes(arch)) { + versionAttributes.bits = Services.appinfo.is64Bit ? 64 : 32; + } else { + versionIdKey += "-arch"; + versionAttributes.arch = arch; + } + let version = Services.appinfo.version; if (/a\d+$/.test(version)) { - versionId = "aboutDialog-version-nightly"; + versionIdKey += "-nightly"; let buildID = Services.appinfo.appBuildID; let year = buildID.slice(0, 4); let month = buildID.slice(4, 6); @@ -65,7 +78,11 @@ function init() { // Use Fluent arguments for append version and the architecture of the build let versionField = document.getElementById("version"); - document.l10n.setAttributes(versionField, versionId, versionAttributes); + document.l10n.setAttributes( + versionField, + versionIdMap.get(versionIdKey), + versionAttributes + ); // Show a release notes link if we have a URL. let relNotesLink = document.getElementById("releasenotes"); diff --git a/firefox-src-part/browser/base/content/browser-init.js b/firefox-src-part/browser/base/content/browser-init.js index bee5309c0..9e80738b5 100644 --- a/firefox-src-part/browser/base/content/browser-init.js +++ b/firefox-src-part/browser/base/content/browser-init.js @@ -371,22 +371,23 @@ var gBrowserInit = { BrowserOffline.init(); CanvasPermissionPromptHelper.init(); WebAuthnPromptHelper.init(); - ContentAnalysis.initialize(document); + + BrowserUtils.callModulesFromCategory( + "browser-window-delayed-startup", + window + ); // Initialize the full zoom setting. // We do this before the session restore service gets initialized so we can // apply full zoom settings to tabs restored by the session restore service. FullZoom.init(); PanelUI.init(shouldSuppressPopupNotifications); - ReportBrokenSite.init(gBrowser); UpdateUrlbarSearchSplitterState(); BookmarkingUI.init(); BrowserSearch.delayedStartupInit(); - SearchUIUtils.init(); gProtectionsHandler.init(); - HomePage.delayedStartup().catch(console.error); let safeMode = document.getElementById("helpSafeMode"); if (Services.appinfo.inSafeMode) { @@ -608,6 +609,8 @@ var gBrowserInit = { ShoppingSidebarManager.ensureInitialized(); + SelectableProfileService?.init(); + SessionStore.promiseAllWindowsRestored.then(() => { this._schedulePerWindowIdleTasks(); document.documentElement.setAttribute("sessionrestored", "true"); @@ -1019,8 +1022,6 @@ var gBrowserInit = { TabletModeUpdater.uninit(); - gTabletModePageCounter.finish(); - CaptivePortalWatcher.uninit(); SidebarController.uninit(); @@ -1037,6 +1038,8 @@ var gBrowserInit = { FirefoxViewHandler.uninit(); + SelectableProfileService?.uninit(); + // Now either cancel delayedStartup, or clean up the services initialized from // it. if (this._boundDelayedStartup) { diff --git a/firefox-src-part/browser/base/content/browser-menubar.js b/firefox-src-part/browser/base/content/browser-menubar.js index eabcac067..d4043eb8c 100644 --- a/firefox-src-part/browser/base/content/browser-menubar.js +++ b/firefox-src-part/browser/base/content/browser-menubar.js @@ -140,7 +140,7 @@ document.addEventListener( updateEditUIVisibility(); break; case "view-menu-popup": - onViewToolbarsPopupShowing(event); + ToolbarContextMenu.onViewToolbarsPopupShowing(event); break; case "pageStyleMenuPopup": gPageStyleMenu.fillPopup(event.target); diff --git a/firefox-src-part/browser/base/content/browser-pageActions.js b/firefox-src-part/browser/base/content/browser-pageActions.js index 0e8139901..1662f74f4 100644 --- a/firefox-src-part/browser/base/content/browser-pageActions.js +++ b/firefox-src-part/browser/base/content/browser-pageActions.js @@ -2,10 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -ChromeUtils.defineESModuleGetters(this, { - SearchUIUtils: "resource:///modules/SearchUIUtils.sys.mjs", -}); - var BrowserPageActions = { _panelNode: null, /** diff --git a/firefox-src-part/browser/base/content/browser-places.js b/firefox-src-part/browser/base/content/browser-places.js index 8ac64ef35..998debfc0 100644 --- a/firefox-src-part/browser/base/content/browser-places.js +++ b/firefox-src-part/browser/base/content/browser-places.js @@ -25,6 +25,15 @@ XPCOMUtils.defineLazyPreferenceGetter( }, console.error); } ); + +// Set by sync after syncing bookmarks successfully once. +XPCOMUtils.defineLazyPreferenceGetter( + this, + "SHOW_MOBILE_BOOKMARKS", + "browser.bookmarks.showMobileBookmarks", + false +); + ChromeUtils.defineESModuleGetters(this, { PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs", RecentlyClosedTabsAndWindowsMenuUtils: @@ -1343,7 +1352,7 @@ var BookmarkingUI = { onPopupShowing: function BUI_onPopupShowing(event) { // Don't handle events for submenus. - if (event.target != event.currentTarget) { + if (event.target.id != "BMB_bookmarksPopup") { return; } @@ -1374,7 +1383,8 @@ var BookmarkingUI = { return; } - this._initMobileBookmarks(document.getElementById("BMB_mobileBookmarks")); + document.getElementById("BMB_mobileBookmarks").hidden = + !SHOW_MOBILE_BOOKMARKS; this.updateLabel( "BMB_viewBookmarksSidebar", @@ -1582,17 +1592,6 @@ var BookmarkingUI = { } }, - // Set by sync after syncing bookmarks successfully once. - MOBILE_BOOKMARKS_PREF: "browser.bookmarks.showMobileBookmarks", - - _shouldShowMobileBookmarks() { - return Services.prefs.getBoolPref(this.MOBILE_BOOKMARKS_PREF, false); - }, - - _initMobileBookmarks(mobileMenuItem) { - mobileMenuItem.hidden = !this._shouldShowMobileBookmarks(); - }, - _uninitView: function BUI__uninitView() { // When an element with a placesView attached is removed and re-inserted, // XBL reapplies the binding causing any kind of issues and possible leaks, @@ -1938,11 +1937,12 @@ var BookmarkingUI = { onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) { // Don't handle events for submenus. - if (event.target != event.currentTarget) { + if (event.target.id != "bookmarksMenuPopup") { return; } - this._initMobileBookmarks(document.getElementById("menu_mobileBookmarks")); + document.getElementById("menu_mobileBookmarks").hidden = + !SHOW_MOBILE_BOOKMARKS; }, showSubView(anchor) { diff --git a/firefox-src-part/browser/base/content/browser-sitePermissionPanel.js b/firefox-src-part/browser/base/content/browser-sitePermissionPanel.js index de7b2cc39..cf85d3f9c 100644 --- a/firefox-src-part/browser/base/content/browser-sitePermissionPanel.js +++ b/firefox-src-part/browser/base/content/browser-sitePermissionPanel.js @@ -164,17 +164,23 @@ var gPermissionPanel = { gBrowser.selectedBrowser ); for (let permission of permissions) { - if (permission.state != SitePermissions.UNKNOWN) { - hasPermissions = true; + // Don't show persisted PROMPT permissions (unless a pref says to). + // These would appear as "Always Ask ✖" which have utility, but might confuse + if ( + permission.state == SitePermissions.UNKNOWN || + (permission.state == SitePermissions.PROMPT && !this._gumShowAlwaysAsk) + ) { + continue; + } + hasPermissions = true; - if ( - permission.state == SitePermissions.BLOCK || - permission.state == SitePermissions.AUTOPLAY_BLOCKED_ALL - ) { - let icon = permissionAnchors[permission.id]; - if (icon) { - icon.setAttribute("showing", "true"); - } + if ( + permission.state == SitePermissions.BLOCK || + permission.state == SitePermissions.AUTOPLAY_BLOCKED_ALL + ) { + let icon = permissionAnchors[permission.id]; + if (icon) { + icon.setAttribute("showing", "true"); } } } @@ -468,8 +474,8 @@ var gPermissionPanel = { if (this._sharingState?.webRTC) { let webrtcState = this._sharingState.webRTC; - // If WebRTC device or screen permissions are in use, we need to find - // the associated permission item to set the sharingState field. + // If WebRTC device or screen are in use, we need to find + // the associated ALLOW permission item to set the sharingState field. for (let id of ["camera", "microphone", "screen"]) { if (webrtcState[id]) { let found = false; @@ -477,14 +483,14 @@ var gPermissionPanel = { let [permId] = permission.id.split( SitePermissions.PERM_KEY_DELIMITER ); - if (permId != id) { + if (permId != id || permission.state != SitePermissions.ALLOW) { continue; } found = true; permission.sharingState = webrtcState[id]; } if (!found) { - // If the permission item we were looking for doesn't exist, + // If the ALLOW permission item we were looking for doesn't exist, // the user has temporarily allowed sharing and we need to add // an item in the permissions array to reflect this. permissions.push({ @@ -524,6 +530,12 @@ var gPermissionPanel = { anchor.appendChild(permContainer); } } else if (["camera", "screen", "microphone", "speaker"].includes(id)) { + if ( + permission.state == SitePermissions.PROMPT && + !this._gumShowAlwaysAsk + ) { + continue; + } item = this._createWebRTCPermissionItem(permission, id, key); if (!item) { continue; @@ -979,8 +991,12 @@ var gPermissionPanel = { return null; } } else if (item) { - // If we have a single-key (not device specific) webRTC permission it - // overrides any existing (device specific) permission items. + if (permission.state == SitePermissions.PROMPT) { + return null; + } + // If we have a single-key (not device specific) webRTC permission + // other than PROMPT, it overrides any existing (device specific) + // permission items. item.remove(); } @@ -1117,3 +1133,10 @@ function hasMicCamGracePeriodsSolely(browser) { } return { micGrace: micGrace && !micGrant, camGrace: camGrace && !camGrant }; } + +XPCOMUtils.defineLazyPreferenceGetter( + gPermissionPanel, + "_gumShowAlwaysAsk", + "permissions.media.show_always_ask.enabled", + false +); diff --git a/firefox-src-part/browser/base/content/browser-siteProtections.js b/firefox-src-part/browser/base/content/browser-siteProtections.js index 280ef4b5c..156f53839 100644 --- a/firefox-src-part/browser/base/content/browser-siteProtections.js +++ b/firefox-src-part/browser/base/content/browser-siteProtections.js @@ -7,6 +7,7 @@ ChromeUtils.defineESModuleGetters(this, { ContentBlockingAllowList: "resource://gre/modules/ContentBlockingAllowList.sys.mjs", + ReportBrokenSite: "resource:///modules/ReportBrokenSite.sys.mjs", SpecialMessageActions: "resource://messaging-system/lib/SpecialMessageActions.sys.mjs", }); @@ -1376,6 +1377,8 @@ var gProtectionsHandler = { if (!this._protectionsPopup) { let wrapper = document.getElementById("template-protections-popup"); this._protectionsPopup = wrapper.content.firstElementChild; + this._protectionsPopup.addEventListener("popupshown", this); + this._protectionsPopup.addEventListener("popuphidden", this); wrapper.replaceWith(wrapper.content); this.maybeSetMilestoneCounterText(); diff --git a/firefox-src-part/browser/base/content/browser.js b/firefox-src-part/browser/base/content/browser.js index a29d7a84e..ccd83c15d 100644 --- a/firefox-src-part/browser/base/content/browser.js +++ b/firefox-src-part/browser/base/content/browser.js @@ -50,7 +50,6 @@ ChromeUtils.defineESModuleGetters(this, { NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs", NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs", nsContextMenu: "chrome://browser/content/nsContextMenu.sys.mjs", - openContextMenu: "chrome://browser/content/nsContextMenu.sys.mjs", OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.sys.mjs", PageActions: "resource:///modules/PageActions.sys.mjs", PageThumbs: "resource://gre/modules/PageThumbs.sys.mjs", @@ -68,7 +67,6 @@ ChromeUtils.defineESModuleGetters(this, { PromptUtils: "resource://gre/modules/PromptUtils.sys.mjs", ReaderMode: "resource://gre/modules/ReaderMode.sys.mjs", ResetPBMPanel: "resource:///modules/ResetPBMPanel.sys.mjs", - ReportBrokenSite: "resource:///modules/ReportBrokenSite.sys.mjs", SafeBrowsing: "resource://gre/modules/SafeBrowsing.sys.mjs", Sanitizer: "resource:///modules/Sanitizer.sys.mjs", SaveToPocket: "chrome://pocket/content/SaveToPocket.sys.mjs", @@ -87,6 +85,7 @@ ChromeUtils.defineESModuleGetters(this, { TabsSetupFlowManager: "resource:///modules/firefox-view-tabs-setup-manager.sys.mjs", TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs", + ToolbarContextMenu: "resource:///modules/ToolbarContextMenu.sys.mjs", TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs", UITour: "resource:///modules/UITour.sys.mjs", UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", @@ -105,6 +104,17 @@ ChromeUtils.defineESModuleGetters(this, { ZoomUI: "resource:///modules/ZoomUI.sys.mjs", }); +// Bug 1894239: We will move this up to ChromeUtils.defineESModuleGetters once +// the MOZ_SELECTABLE_PROFILES flag is removed +ChromeUtils.defineLazyGetter(this, "SelectableProfileService", () => { + if (!AppConstants.MOZ_SELECTABLE_PROFILES) { + return null; + } + return ChromeUtils.importESModule( + "resource:///modules/profiles/SelectableProfileService.sys.mjs" + ).SelectableProfileService; +}); + ChromeUtils.defineLazyGetter(this, "fxAccounts", () => { return ChromeUtils.importESModule( "resource://gre/modules/FxAccounts.sys.mjs" @@ -563,13 +573,6 @@ XPCOMUtils.defineLazyPreferenceGetter( false ); -XPCOMUtils.defineLazyPreferenceGetter( - this, - "gAlwaysOpenPanel", - "browser.download.alwaysOpenPanel", - true -); - XPCOMUtils.defineLazyPreferenceGetter( this, "gMiddleClickNewTabUsesPasteboard", @@ -1616,7 +1619,7 @@ var BrowserOnClick = { browsingContext.fixupAndLoadURIString(blockedInfo.uri, { triggeringPrincipal, - flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, + loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, }); }, }; @@ -2521,7 +2524,9 @@ function FillHistoryMenu(event) { entry.hasUserInteraction === false && // Always allow going to the first and last navigation points. j != end - 1 && - j != start + j != start && + // Always display the current entry + j != index ) { continue; } @@ -3401,8 +3406,6 @@ var XULBrowserWindow = { aFlags ); - gTabletModePageCounter.inc(); - this._updateElementsForContentType(); this._updateMacUserActivity(window, aLocationURI, aWebProgress); @@ -4549,170 +4552,6 @@ function showFullScreenViewContextMenuItems(popup) { } } -function onViewToolbarsPopupShowing(aEvent, aInsertPoint) { - var popup = aEvent.target; - - // triggerNode can be a nested child element of a toolbaritem. - let toolbarItem = popup.triggerNode; - while (toolbarItem) { - let localName = toolbarItem.localName; - if (localName == "toolbar") { - toolbarItem = null; - break; - } - if (localName == "toolbarpaletteitem") { - toolbarItem = toolbarItem.firstElementChild; - break; - } - if (localName == "menupopup") { - aEvent.preventDefault(); - aEvent.stopPropagation(); - return; - } - let parent = toolbarItem.parentElement; - if (parent) { - if ( - parent.classList.contains("customization-target") || - parent.getAttribute("overflowfortoolbar") || // Needs to work in the overflow list as well. - parent.localName == "toolbarpaletteitem" || - parent.localName == "toolbar" - ) { - break; - } - } - toolbarItem = parent; - } - - // Empty the menu - for (var i = popup.children.length - 1; i >= 0; --i) { - var deadItem = popup.children[i]; - if (deadItem.hasAttribute("toolbarId")) { - popup.removeChild(deadItem); - } - } - - MozXULElement.insertFTLIfNeeded("browser/toolbarContextMenu.ftl"); - let firstMenuItem = aInsertPoint || popup.firstElementChild; - let toolbarNodes = gNavToolbox.querySelectorAll("toolbar"); - for (let toolbar of toolbarNodes) { - if (!toolbar.hasAttribute("toolbarname")) { - continue; - } - - if (toolbar.id == "PersonalToolbar") { - let menu = BookmarkingUI.buildBookmarksToolbarSubmenu(toolbar); - popup.insertBefore(menu, firstMenuItem); - } else { - let menuItem = document.createXULElement("menuitem"); - menuItem.setAttribute("id", "toggle_" + toolbar.id); - menuItem.setAttribute("toolbarId", toolbar.id); - menuItem.setAttribute("type", "checkbox"); - menuItem.setAttribute("label", toolbar.getAttribute("toolbarname")); - let hidingAttribute = - toolbar.getAttribute("type") == "menubar" ? "autohide" : "collapsed"; - menuItem.setAttribute( - "checked", - toolbar.getAttribute(hidingAttribute) != "true" - ); - menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey")); - if (popup.id != "toolbar-context-menu") { - menuItem.setAttribute("key", toolbar.getAttribute("key")); - } - - popup.insertBefore(menuItem, firstMenuItem); - menuItem.addEventListener("command", onViewToolbarCommand); - } - } - - let moveToPanel = popup.querySelector(".customize-context-moveToPanel"); - let removeFromToolbar = popup.querySelector( - ".customize-context-removeFromToolbar" - ); - // Show/hide fullscreen context menu items and set the - // autohide item's checked state to mirror the autohide pref. - showFullScreenViewContextMenuItems(popup); - // View -> Toolbars menu doesn't have the moveToPanel or removeFromToolbar items. - if (!moveToPanel || !removeFromToolbar) { - return; - } - - let showTabStripItems = toolbarItem?.id == "tabbrowser-tabs"; - for (let node of popup.querySelectorAll( - 'menuitem[contexttype="toolbaritem"]' - )) { - node.hidden = showTabStripItems; - } - - for (let node of popup.querySelectorAll('menuitem[contexttype="tabbar"]')) { - node.hidden = !showTabStripItems; - } - - document - .getElementById("toolbar-context-menu") - .querySelectorAll("[data-lazy-l10n-id]") - .forEach(el => { - el.setAttribute("data-l10n-id", el.getAttribute("data-lazy-l10n-id")); - el.removeAttribute("data-lazy-l10n-id"); - }); - - // The "normal" toolbar items menu separator is hidden because it's unused - // when hiding the "moveToPanel" and "removeFromToolbar" items on flexible - // space items. But we need to ensure its hidden state is reset in the case - // the context menu is subsequently opened on a non-flexible space item. - let menuSeparator = document.getElementById("toolbarItemsMenuSeparator"); - menuSeparator.hidden = false; - - document.getElementById("toolbarNavigatorItemsMenuSeparator").hidden = - !showTabStripItems; - - if ( - !CustomizationHandler.isCustomizing() && - CustomizableUI.isSpecialWidget(toolbarItem?.id || "") - ) { - moveToPanel.hidden = true; - removeFromToolbar.hidden = true; - menuSeparator.hidden = !showTabStripItems; - } - - if (showTabStripItems) { - let multipleTabsSelected = !!gBrowser.multiSelectedTabsCount; - document.getElementById("toolbar-context-bookmarkSelectedTabs").hidden = - !multipleTabsSelected; - document.getElementById("toolbar-context-bookmarkSelectedTab").hidden = - multipleTabsSelected; - document.getElementById("toolbar-context-reloadSelectedTabs").hidden = - !multipleTabsSelected; - document.getElementById("toolbar-context-reloadSelectedTab").hidden = - multipleTabsSelected; - document.getElementById("toolbar-context-selectAllTabs").disabled = - gBrowser.allTabsSelected(); - document.getElementById("toolbar-context-undoCloseTab").disabled = - SessionStore.getClosedTabCount() == 0; - return; - } - - // fxms-bmb-button is a Firefox Messaging System Bookmarks bar button - let removable = !toolbarItem?.classList?.contains("fxms-bmb-button"); - let movable = - toolbarItem?.id && - removable && - !toolbarItem?.classList?.contains("fxms-bmb-button") && - CustomizableUI.isWidgetRemovable(toolbarItem); - if (movable) { - if (CustomizableUI.isSpecialWidget(toolbarItem.id)) { - moveToPanel.setAttribute("disabled", true); - } else { - moveToPanel.removeAttribute("disabled"); - } - removeFromToolbar.removeAttribute("disabled"); - } else { - if (removable) { - removeFromToolbar.setAttribute("disabled", true); - } - moveToPanel.setAttribute("disabled", true); - } -} - function onViewToolbarCommand(aEvent) { let node = aEvent.originalTarget; let menuId; @@ -4871,36 +4710,6 @@ var TabletModeUpdater = { }, }; -var gTabletModePageCounter = { - enabled: false, - inc() { - this.enabled = AppConstants.platform == "win"; - if (!this.enabled) { - this.inc = () => {}; - return; - } - this.inc = this._realInc; - this.inc(); - }, - - _desktopCount: 0, - _tabletCount: 0, - _realInc() { - let inTabletMode = document.documentElement.hasAttribute("tabletmode"); - this[inTabletMode ? "_tabletCount" : "_desktopCount"]++; - }, - - finish() { - if (this.enabled) { - let histogram = Services.telemetry.getKeyedHistogramById( - "FX_TABLETMODE_PAGE_LOAD" - ); - histogram.add("tablet", this._tabletCount); - histogram.add("desktop", this._desktopCount); - } - }, -}; - function displaySecurityInfo() { BrowserCommands.pageInfo(null, "securityTab"); } @@ -5401,135 +5210,6 @@ function handleDroppedLink( } } -var ToolbarContextMenu = { - updateDownloadsAutoHide(popup) { - let checkbox = document.getElementById( - "toolbar-context-autohide-downloads-button" - ); - let isDownloads = - popup.triggerNode && - ["downloads-button", "wrapper-downloads-button"].includes( - popup.triggerNode.id - ); - checkbox.hidden = !isDownloads; - if (DownloadsButton.autoHideDownloadsButton) { - checkbox.setAttribute("checked", "true"); - } else { - checkbox.removeAttribute("checked"); - } - }, - - onDownloadsAutoHideChange(event) { - let autoHide = event.target.getAttribute("checked") == "true"; - Services.prefs.setBoolPref("browser.download.autohideButton", autoHide); - }, - - updateDownloadsAlwaysOpenPanel(popup) { - let separator = document.getElementById( - "toolbarDownloadsAnchorMenuSeparator" - ); - let checkbox = document.getElementById( - "toolbar-context-always-open-downloads-panel" - ); - let isDownloads = - popup.triggerNode && - ["downloads-button", "wrapper-downloads-button"].includes( - popup.triggerNode.id - ); - separator.hidden = checkbox.hidden = !isDownloads; - gAlwaysOpenPanel - ? checkbox.setAttribute("checked", "true") - : checkbox.removeAttribute("checked"); - }, - - onDownloadsAlwaysOpenPanelChange(event) { - let alwaysOpen = event.target.getAttribute("checked") == "true"; - Services.prefs.setBoolPref("browser.download.alwaysOpenPanel", alwaysOpen); - }, - - _getUnwrappedTriggerNode(popup) { - // Toolbar buttons are wrapped in customize mode. Unwrap if necessary. - let { triggerNode } = popup; - if (triggerNode && gCustomizeMode.isWrappedToolbarItem(triggerNode)) { - return triggerNode.firstElementChild; - } - return triggerNode; - }, - - _getExtensionId(popup) { - let node = this._getUnwrappedTriggerNode(popup); - return node && node.getAttribute("data-extensionid"); - }, - - _getWidgetId(popup) { - let node = this._getUnwrappedTriggerNode(popup); - return node?.closest(".unified-extensions-item")?.id; - }, - - async updateExtension(popup, event) { - let removeExtension = popup.querySelector( - ".customize-context-removeExtension" - ); - let manageExtension = popup.querySelector( - ".customize-context-manageExtension" - ); - let reportExtension = popup.querySelector( - ".customize-context-reportExtension" - ); - let pinToToolbar = popup.querySelector(".customize-context-pinToToolbar"); - let separator = reportExtension.nextElementSibling; - let id = this._getExtensionId(popup); - let addon = id && (await AddonManager.getAddonByID(id)); - - for (let element of [removeExtension, manageExtension, separator]) { - element.hidden = !addon; - } - - if (pinToToolbar) { - pinToToolbar.hidden = !addon; - } - - reportExtension.hidden = !addon || !gAddonAbuseReportEnabled; - - if (addon) { - popup.querySelector(".customize-context-moveToPanel").hidden = true; - popup.querySelector(".customize-context-removeFromToolbar").hidden = true; - - if (pinToToolbar) { - let widgetId = this._getWidgetId(popup); - if (widgetId) { - let area = CustomizableUI.getPlacementOfWidget(widgetId).area; - let inToolbar = area != CustomizableUI.AREA_ADDONS; - pinToToolbar.setAttribute("checked", inToolbar); - } - } - - removeExtension.disabled = !( - addon.permissions & AddonManager.PERM_CAN_UNINSTALL - ); - - if (event?.target?.id === "toolbar-context-menu") { - ExtensionsUI.originControlsMenu(popup, id); - } - } - }, - - async removeExtensionForContextAction(popup) { - let id = this._getExtensionId(popup); - await BrowserAddonUI.removeAddon(id, "browserAction"); - }, - - async reportExtensionForContextAction(popup, reportEntryPoint) { - let id = this._getExtensionId(popup); - await BrowserAddonUI.reportAddon(id, reportEntryPoint); - }, - - async openAboutAddonsForContextAction(popup) { - let id = this._getExtensionId(popup); - await BrowserAddonUI.manageAddon(id, "browserAction"); - }, -}; - // Note that this is also called from non-browser windows on OSX, which do // share menu items but not much else. See nonbrowser-mac.js. var BrowserOffline = { diff --git a/firefox-src-part/browser/base/content/main-popupset.inc.xhtml b/firefox-src-part/browser/base/content/main-popupset.inc.xhtml index 7c0734fbf..bbe6d8bde 100644 --- a/firefox-src-part/browser/base/content/main-popupset.inc.xhtml +++ b/firefox-src-part/browser/base/content/main-popupset.inc.xhtml @@ -109,14 +109,21 @@ noautofocus="true" hidden="true" /> - +