diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
commit | 40a355a42d4a9444dc753c04c6608dade2f06a23 (patch) | |
tree | 871fc667d2de662f171103ce5ec067014ef85e61 /browser/base | |
parent | Adding upstream version 124.0.1. (diff) | |
download | firefox-40a355a42d4a9444dc753c04c6608dade2f06a23.tar.xz firefox-40a355a42d4a9444dc753c04c6608dade2f06a23.zip |
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/base')
79 files changed, 2371 insertions, 2207 deletions
diff --git a/browser/base/content/appmenu-viewcache.inc.xhtml b/browser/base/content/appmenu-viewcache.inc.xhtml index 07b1765f18..04bba182fb 100644 --- a/browser/base/content/appmenu-viewcache.inc.xhtml +++ b/browser/base/content/appmenu-viewcache.inc.xhtml @@ -38,6 +38,13 @@ </vbox> </toolbarbutton> </toolbaritem> + <toolbarbutton id="appMenu-profiles-button" + class="subviewbutton subviewbutton-nav" + data-l10n-id="appmenuitem-profiles" + data-l10n-args='{ "profilename": "" }' + closemenu="none" + oncommand="gProfiles.updateView(this)" + hidden="true"/> <toolbarseparator id="appMenu-fxa-separator" class="proton-zap"/> <toolbarbutton id="appMenu-new-tab-button2" class="subviewbutton" @@ -212,6 +219,39 @@ oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/> </panelview> + <panelview id="PanelUI-profiles" flex="1"> + <vbox class="panel-subview-body"> + <vbox id="current-profile"> + <image id="profile-icon-image"></image> + <h2 id="profile-name"></h2> + <hbox id="this-profile-buttons"> + <toolbarbutton id="profiles-edit-this-delete-button" + class="subviewbutton toolbarbutton-1" + oncommand="switchToTabHavingURI('about:profilemanager', true)"/> + <toolbarbutton id="profiles-delete-this-profile-button" + class="subviewbutton toolbarbutton-1" + oncommand="switchToTabHavingURI('about:profilemanager', true)"/> + </hbox> + </vbox> + <toolbarseparator/> + <vbox id="profiles-list"></vbox> + <toolbarseparator/> + <toolbarbutton id="profiles-close-profile-button" + class="subviewbutton" + data-l10n-id="appmenu-close-profile" + data-l10n-args='{ "profilename": "" }' + oncommand=""/> + <toolbarbutton id="profiles-create-profile-button" + class="subviewbutton" + data-l10n-id="appmenu-create-profile" + oncommand="switchToTabHavingURI('about:profilemanager', true)"/> + <toolbarbutton id="profiles-manage-profiles-button" + class="subviewbutton" + data-l10n-id="appmenu-manage-profiles" + oncommand="switchToTabHavingURI('about:profilemanager', true)"/> + </vbox> + </panelview> + <panelview id="appMenu-library-recentlyClosedTabs"/> <panelview id="appMenu-library-recentlyClosedWindows"/> diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc index 9230c07f6a..ff4015e3d4 100644 --- a/browser/base/content/browser-context.inc +++ b/browser/base/content/browser-context.inc @@ -313,6 +313,9 @@ <menuitem id="context-selectall" data-l10n-id="text-action-select-all" command="cmd_selectAll"/> + <menuitem id="context-pdfjs-highlight-selection" + data-l10n-id="text-action-highlight-selection" + oncommand="gContextMenu.pdfJSCmd('highlightSelection');"/> <menuitem id="context-reveal-password" type="checkbox" data-l10n-id="main-context-menu-reveal-password" @@ -358,7 +361,8 @@ <menuitem id="context-searchselect-private" oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms, true, this.principal, this.csp, event);"/> <menuitem id="context-translate-selection" - data-l10n-id="main-context-menu-translate-selection"/> + data-l10n-id="main-context-menu-translate-selection" + oncommand="gContextMenu.openSelectTranslationsPanel(event);"/> <menuseparator id="frame-sep"/> <menu id="frame" data-l10n-id="main-context-menu-frame"> diff --git a/browser/base/content/browser-ctrlTab.js b/browser/base/content/browser-ctrlTab.js index 31617f13a3..d4d79a6886 100644 --- a/browser/base/content/browser-ctrlTab.js +++ b/browser/base/content/browser-ctrlTab.js @@ -581,7 +581,7 @@ var ctrlTab = { return; } - Services.els.addSystemEventListener(document, "keyup", this, false); + document.addEventListener("keyup", this, { mozSystemGroup: true }); let tabs = gBrowser.visibleTabs; if (tabs.length > 2) { @@ -706,12 +706,7 @@ var ctrlTab = { event.stopPropagation(); if (event.keyCode === event.DOM_VK_CONTROL) { - Services.els.removeSystemEventListener( - document, - "keyup", - this, - false - ); + document.removeEventListener("keyup", this, { mozSystemGroup: true }); if (this.isOpen) { this.pick(); @@ -787,9 +782,9 @@ var ctrlTab = { tabContainer[toggleEventListener]("TabShow", this); if (enable) { - Services.els.addSystemEventListener(document, "keydown", this, false); + document.addEventListener("keydown", this, { mozSystemGroup: true }); } else { - Services.els.removeSystemEventListener(document, "keydown", this, false); + document.removeEventListener("keydown", this, { mozSystemGroup: true }); } document[toggleEventListener]("keypress", this); gBrowser.tabbox.handleCtrlTab = !enable; diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index 6992d22069..58e61f7bf7 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -392,7 +392,7 @@ var PlacesCommandHook = { */ async bookmarkPage() { let browser = gBrowser.selectedBrowser; - let url = URL.fromURI(browser.currentURI); + let url = URL.fromURI(Services.io.createExposableURI(browser.currentURI)); let info = await PlacesUtils.bookmarks.fetch({ url }); let isNewBookmark = !info; let showEditUI = !isNewBookmark || StarUI.showForNewBookmarks; @@ -490,6 +490,21 @@ var PlacesCommandHook = { }, /** + * Bookmarks the given tabs loaded in the current browser. + * @param {Array} tabs + * If no given tabs, bookmark all current tabs. + */ + async bookmarkTabs(tabs) { + tabs = tabs ?? gBrowser.visibleTabs.filter(tab => !tab.pinned); + let pages = PlacesCommandHook.getUniquePages(tabs).map( + // Bookmark exposable url. + page => + Object.assign(page, { uri: Services.io.createExposableURI(page.uri) }) + ); + await PlacesUIUtils.showBookmarkPagesDialog(pages); + }, + + /** * List of nsIURI objects characterizing tabs given in param. * Duplicates are discarded. */ @@ -511,24 +526,6 @@ var PlacesCommandHook = { }, /** - * List of nsIURI objects characterizing the tabs currently open in the - * browser, modulo pinned tabs. The URIs will be in the order in which their - * corresponding tabs appeared and duplicates are discarded. - */ - get uniqueCurrentPages() { - let visibleUnpinnedTabs = gBrowser.visibleTabs.filter(tab => !tab.pinned); - return this.getUniquePages(visibleUnpinnedTabs); - }, - - /** - * List of nsIURI objects characterizing the tabs currently - * selected in the window. Duplicates are discarded. - */ - get uniqueSelectedPages() { - return this.getUniquePages(gBrowser.selectedTabs); - }, - - /** * Opens the Places Organizer. * @param {String} item The item to select in the organizer window, * options are (case sensitive): diff --git a/browser/base/content/browser-profiles.js b/browser/base/content/browser-profiles.js new file mode 100644 index 0000000000..72eca39e44 --- /dev/null +++ b/browser/base/content/browser-profiles.js @@ -0,0 +1,99 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// This file is loaded into the browser window scope. +/* eslint-env mozilla/browser-window */ + +XPCOMUtils.defineLazyServiceGetter( + this, + "ProfileService", + "@mozilla.org/toolkit/profile-service;1", + "nsIToolkitProfileService" +); + +var gProfiles = { + init() { + XPCOMUtils.defineLazyPreferenceGetter( + this, + "PROFILES_ENABLED", + "browser.profiles.enabled", + false, + this.toggleProfileButtonsVisibility.bind(this) + ); + + if (!this.PROFILES_ENABLED) { + return; + } + + this.toggleProfileButtonsVisibility(); + }, + + toggleProfileButtonsVisibility() { + let profilesButton = PanelMultiView.getViewNode( + document, + "appMenu-profiles-button" + ); + + profilesButton.hidden = !this.PROFILES_ENABLED; + + if (this.PROFILES_ENABLED) { + document.l10n.setArgs(profilesButton, { + profilename: ProfileService.currentProfile?.name ?? "", + }); + } + }, + + updateView(panel) { + this.populateSubView(); + PanelUI.showSubView("PanelUI-profiles", panel); + }, + + async populateSubView() { + let closeProfileButton = PanelMultiView.getViewNode( + document, + "profiles-close-profile-button" + ); + document.l10n.setArgs(closeProfileButton, { + profilename: ProfileService.currentProfile?.name ?? "", + }); + + let profileIconEl = PanelMultiView.getViewNode( + document, + "profile-icon-image" + ); + profileIconEl.style.listStyleImage = `url(${ + ProfileService.currentProfile?.iconURL ?? + "chrome://branding/content/icon64.png" + })`; + + let profileNameEl = PanelMultiView.getViewNode(document, "profile-name"); + profileNameEl.textContent = ProfileService.currentProfile?.name ?? ""; + + let profilesList = PanelMultiView.getViewNode( + document, + "PanelUI-profiles" + ).querySelector("#profiles-list"); + while (profilesList.lastElementChild) { + profilesList.lastElementChild.remove(); + } + + for (let profile of ProfileService.profiles) { + if (profile === ProfileService.currentProfile) { + continue; + } + + let button = document.createXULElement("toolbarbutton"); + button.setAttribute("label", profile.name); + button.className = "subviewbutton subviewbutton-iconic"; + button.style.listStyleImage = `url(${ + profile.iconURL ?? "chrome://branding/content/icon16.png" + })`; + button.onclick = () => { + Services.startup.createInstanceWithProfile(profile); + }; + + profilesList.appendChild(button); + } + }, +}; diff --git a/browser/base/content/browser-sets.inc b/browser/base/content/browser-sets.inc index 019b3cc469..090e94b684 100644 --- a/browser/base/content/browser-sets.inc +++ b/browser/base/content/browser-sets.inc @@ -52,14 +52,14 @@ <command id="cmd_findSelection" oncommand="gLazyFindCommand('onFindSelectionCommand')"/> #endif <command id="cmd_reportBrokenSite" oncommand="ReportBrokenSite.open(event);"/> - <command id="cmd_translate" oncommand="TranslationsPanel.open(event);"/> + <command id="cmd_translate" oncommand="FullPageTranslationsPanel.open(event);"/> <!-- work-around bug 392512 --> <command id="Browser:AddBookmarkAs" oncommand="PlacesCommandHook.bookmarkPage();"/> <command id="Browser:SearchBookmarks" oncommand="PlacesCommandHook.searchBookmarks();"/> <command id="Browser:BookmarkAllTabs" - oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueCurrentPages);"/> + oncommand="PlacesCommandHook.bookmarkTabs();"/> <command id="Browser:Back" oncommand="BrowserBack();" disabled="true"/> <command id="Browser:BackOrBackDuplicate" oncommand="BrowserBack(event);" disabled="true"> <observes element="Browser:Back" attribute="disabled"/> @@ -270,7 +270,7 @@ <key id="addBookmarkAsKb" data-l10n-id="bookmark-this-page-shortcut" command="Browser:AddBookmarkAs" modifiers="accel"/> <key id="bookmarkAllTabsKb" data-l10n-id="bookmark-this-page-shortcut" - oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueCurrentPages);" + oncommand="PlacesCommandHook.bookmarkTabs();" modifiers="accel,shift"/> <key id="manBookmarkKb" data-l10n-id="bookmark-show-library-shortcut" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/> <key id="viewBookmarksSidebarKb" diff --git a/browser/base/content/browser-sidebar.js b/browser/base/content/browser-sidebar.js index ea6457a5c4..2d730700a6 100644 --- a/browser/base/content/browser-sidebar.js +++ b/browser/base/content/browser-sidebar.js @@ -266,7 +266,7 @@ var SidebarUI = { }, updateShortcut({ keyId }) { - let menuitem = this._switcherPanel.querySelector(`[key="${keyId}"]`); + let menuitem = this._switcherPanel?.querySelector(`[key="${keyId}"]`); if (!menuitem) { // If the menu item doesn't exist yet then the accel text will be set correctly // upon creation so there's nothing to do now. diff --git a/browser/base/content/browser-siteProtections.js b/browser/base/content/browser-siteProtections.js index 5364aa74cd..c44b4d3e8e 100644 --- a/browser/base/content/browser-siteProtections.js +++ b/browser/base/content/browser-siteProtections.js @@ -31,8 +31,6 @@ class ProtectionCategory { * @param {Object} options - Category options. * @param {string} options.prefEnabled - ID of pref which controls the * category enabled state. - * @param {string} [options.reportBreakageLabel] - Telemetry label to use when - * users report TP breakage. Defaults to protection ID. * @param {string} [options.l10nId] - Identifier l10n strings are keyed under * for this category. Defaults to protection ID. * @param {Object} flags - Flags for this category to look for in the content @@ -51,7 +49,7 @@ class ProtectionCategory { */ constructor( id, - { prefEnabled, reportBreakageLabel, l10nId }, + { prefEnabled, l10nId }, { load, block, @@ -61,7 +59,6 @@ class ProtectionCategory { ) { this._id = id; this.prefEnabled = prefEnabled; - this._reportBreakageLabel = reportBreakageLabel || id; this._flags = { load, block, shim, allow }; @@ -112,10 +109,6 @@ class ProtectionCategory { return this._enabled; } - get reportBreakageLabel() { - return this._reportBreakageLabel; - } - /** * Get the category item associated with this protection from the main * protections panel. @@ -333,7 +326,6 @@ let Fingerprinting = "fingerprinters", { prefEnabled: "privacy.trackingprotection.fingerprinting.enabled", - reportBreakageLabel: "fingerprinting", }, { load: Ci.nsIWebProgressListener.STATE_LOADED_FINGERPRINTING_CONTENT, @@ -411,7 +403,6 @@ let Cryptomining = new ProtectionCategory( "cryptominers", { prefEnabled: "privacy.trackingprotection.cryptomining.enabled", - reportBreakageLabel: "cryptomining", }, { load: Ci.nsIWebProgressListener.STATE_LOADED_CRYPTOMINING_CONTENT, @@ -427,7 +418,6 @@ let TrackingProtection = { l10nId: "trackingContent", prefEnabled: "privacy.trackingprotection.enabled", - reportBreakageLabel: "trackingprotection", }, { load: null, @@ -699,28 +689,6 @@ let ThirdPartyCookies = ); } - get reportBreakageLabel() { - switch (this.behaviorPref) { - case Ci.nsICookieService.BEHAVIOR_ACCEPT: - return "nocookiesblocked"; - case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN: - return "allthirdpartycookiesblocked"; - case Ci.nsICookieService.BEHAVIOR_REJECT: - return "allcookiesblocked"; - case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN: - return "cookiesfromunvisitedsitesblocked"; - default: - console.error( - `Error: Unknown cookieBehavior pref observed: ${this.behaviorPref}` - ); - // fall through - case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER: - return "cookierestrictions"; - case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: - return "cookierestrictionsforeignpartitioned"; - } - } - isBlocking(state) { return ( (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) != @@ -1099,7 +1067,6 @@ let SocialTracking = { l10nId: "socialMediaTrackers", prefEnabled: "privacy.socialtracking.block_cookies.enabled", - reportBreakageLabel: "socialtracking", }, { load: Ci.nsIWebProgressListener.STATE_LOADED_SOCIALTRACKING_CONTENT, @@ -1406,7 +1373,6 @@ let cookieBannerHandling = new (class { * Utility object to handle manipulations of the protections indicators in the UI */ var gProtectionsHandler = { - PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url", PREF_CB_CATEGORY: "browser.contentblocking.category", _protectionsPopup: null, @@ -1456,18 +1422,6 @@ var gProtectionsHandler = { "protections-popup-mainView-panel-header-span" )); }, - get _protectionsPopupTPSwitchBreakageLink() { - delete this._protectionsPopupTPSwitchBreakageLink; - return (this._protectionsPopupTPSwitchBreakageLink = - document.getElementById("protections-popup-tp-switch-breakage-link")); - }, - get _protectionsPopupTPSwitchBreakageFixedLink() { - delete this._protectionsPopupTPSwitchBreakageFixedLink; - return (this._protectionsPopupTPSwitchBreakageFixedLink = - document.getElementById( - "protections-popup-tp-switch-breakage-fixed-link" - )); - }, get _protectionsPopupTPSwitch() { delete this._protectionsPopupTPSwitch; return (this._protectionsPopupTPSwitch = document.getElementById( @@ -1524,28 +1478,6 @@ var gProtectionsHandler = { "protections-popup-footer-protection-type-label" )); }, - get _protectionsPopupSiteNotWorkingTPSwitch() { - delete this._protectionsPopupSiteNotWorkingTPSwitch; - return (this._protectionsPopupSiteNotWorkingTPSwitch = - document.getElementById("protections-popup-siteNotWorking-tp-switch")); - }, - get _protectionsPopupSiteNotWorkingReportError() { - delete this._protectionsPopupSiteNotWorkingReportError; - return (this._protectionsPopupSiteNotWorkingReportError = - document.getElementById("protections-popup-sendReportView-report-error")); - }, - get _protectionsPopupSendReportURL() { - delete this._protectionsPopupSendReportURL; - return (this._protectionsPopupSendReportURL = document.getElementById( - "protections-popup-sendReportView-collection-url" - )); - }, - get _protectionsPopupSendReportButton() { - delete this._protectionsPopupSendReportButton; - return (this._protectionsPopupSendReportButton = document.getElementById( - "protections-popup-sendReportView-submit" - )); - }, get _trackingProtectionIconTooltipLabel() { delete this._trackingProtectionIconTooltipLabel; return (this._trackingProtectionIconTooltipLabel = document.getElementById( @@ -1580,13 +1512,6 @@ var gProtectionsHandler = { )); }, - get _siteNotWorkingIssueListFonts() { - delete this._siteNotWorkingIssueListFonts; - return (this._siteNotWorkingIssueListFonts = document.getElementById( - "protections-panel-site-not-working-view-issue-list-fonts" - )); - }, - // A list of blockers that will be displayed in the categories list // when blockable content is detected. A blocker must be an object // with at least the following two properties: @@ -1811,10 +1736,6 @@ var gProtectionsHandler = { window.addEventListener("focus", this, true); this._protectionsPopupTPSwitch.addEventListener("toggle", this); - this._protectionsPopupSiteNotWorkingTPSwitch.addEventListener( - "toggle", - this - ); // Insert the info message if needed. This will be shown once and then // remain collapsed. @@ -1834,10 +1755,6 @@ var gProtectionsHandler = { if (event.target == this._protectionsPopup) { window.removeEventListener("focus", this, true); this._protectionsPopupTPSwitch.removeEventListener("toggle", this); - this._protectionsPopupSiteNotWorkingTPSwitch.removeEventListener( - "toggle", - this - ); } }, @@ -2163,7 +2080,7 @@ var gProtectionsHandler = { let currentlyEnabled = !this.hasException; - this.updateProtectionsToggles(currentlyEnabled); + this.updateProtectionsToggle(currentlyEnabled); this._notBlockingWhyLink.setAttribute( "tooltip", @@ -2172,9 +2089,6 @@ var gProtectionsHandler = { : "protections-popup-not-blocking-why-etp-off-tooltip" ); - // Toggle the breakage link according to the current enable state. - this.toggleBreakageLink(); - // Update the tooltip of the blocked tracker counter. this.maybeUpdateEarliestRecordedDateTooltip(); @@ -2196,28 +2110,23 @@ var gProtectionsHandler = { }, /** - * Updates the "pressed" state and labels for both toggles in the different - * panel subviews. + * Updates the "pressed" state and labels for the toggle * - * @param {boolean} isPressed - Whether or not the toggles should be pressed. + * @param {boolean} isPressed - Whether or not the toggle should be pressed. * True if ETP is enabled for a given site. */ - updateProtectionsToggles(isPressed) { + updateProtectionsToggle(isPressed) { let host = gIdentityHandler.getHostForDisplay(); - for (let toggle of [ - this._protectionsPopupTPSwitch, - this._protectionsPopupSiteNotWorkingTPSwitch, - ]) { - toggle.toggleAttribute("pressed", isPressed); - toggle.toggleAttribute("disabled", !!this._TPSwitchCommanding); - document.l10n.setAttributes( - toggle, - isPressed - ? "protections-panel-etp-toggle-on" - : "protections-panel-etp-toggle-off", - { host } - ); - } + let toggle = this._protectionsPopupTPSwitch; + toggle.toggleAttribute("pressed", isPressed); + toggle.toggleAttribute("disabled", !!this._TPSwitchCommanding); + document.l10n.setAttributes( + toggle, + isPressed + ? "protections-panel-etp-toggle-on" + : "protections-panel-etp-toggle-off", + { host } + ); }, /* @@ -2313,10 +2222,7 @@ var gProtectionsHandler = { let newExceptionState = this._protectionsPopup.toggleAttribute("hasException"); - this.updateProtectionsToggles(!newExceptionState); - - // Toggle the breakage link if needed. - this.toggleBreakageLink(); + this.updateProtectionsToggle(!newExceptionState); // Change the tooltip of the tracking protection icon. if (newExceptionState) { @@ -2523,158 +2429,6 @@ var gProtectionsHandler = { ).catch(console.error); }, - showSiteNotWorkingView() { - // Only show the Fonts item if we are restricting font visibility - if (this._fontVisibilityTrackingProtection >= 3) { - this._siteNotWorkingIssueListFonts.setAttribute("hidden", "true"); - } else { - this._siteNotWorkingIssueListFonts.removeAttribute("hidden"); - } - - this._protectionsPopupMultiView.showSubView( - "protections-popup-siteNotWorkingView" - ); - }, - - showSendReportView() { - // Save this URI to make sure that the user really only submits the location - // they see in the report breakage dialog. - this.reportURI = gBrowser.currentURI; - let urlWithoutQuery = this.reportURI.asciiSpec.replace( - "?" + this.reportURI.query, - "" - ); - let commentsTextarea = document.getElementById( - "protections-popup-sendReportView-collection-comments" - ); - commentsTextarea.value = ""; - this._protectionsPopupSendReportURL.value = urlWithoutQuery; - this._protectionsPopupSiteNotWorkingReportError.hidden = true; - this._protectionsPopupMultiView.showSubView( - "protections-popup-sendReportView" - ); - }, - - toggleBreakageLink() { - // The breakage link will only be shown if tracking protection is enabled - // for the site and the TP toggle state is on. And we won't show the - // link as toggling TP switch to On from Off. In order to do so, we need to - // know the previous TP state. We check the ContentBlockingAllowList instead - // of 'hasException' attribute of the protection popup for the previous - // since the 'hasException' will also be toggled as well as toggling the TP - // switch. We won't be able to know the previous TP state through the - // 'hasException' attribute. So we fallback to check the - // ContentBlockingAllowList here. - this._protectionsPopupTPSwitchBreakageLink.hidden = - ContentBlockingAllowList.includes(gBrowser.selectedBrowser) || - !this.anyBlocking || - !this._protectionsPopupTPSwitch.hasAttribute("pressed"); - // The "Site Fixed?" link behaves similarly but for the opposite state. - this._protectionsPopupTPSwitchBreakageFixedLink.hidden = - !ContentBlockingAllowList.includes(gBrowser.selectedBrowser) || - this._protectionsPopupTPSwitch.hasAttribute("pressed"); - }, - - submitBreakageReport(uri) { - let reportEndpoint = Services.prefs.getStringPref( - this.PREF_REPORT_BREAKAGE_URL - ); - if (!reportEndpoint) { - return; - } - - let commentsTextarea = document.getElementById( - "protections-popup-sendReportView-collection-comments" - ); - - let formData = new FormData(); - formData.set("title", uri.host); - - // Leave the ? at the end of the URL to signify that this URL had its query stripped. - let urlWithoutQuery = uri.asciiSpec.replace(uri.query, ""); - let body = `Full URL: ${urlWithoutQuery}\n`; - body += `userAgent: ${navigator.userAgent}\n`; - - body += "\n**Preferences**\n"; - body += `${TrackingProtection.prefEnabled}: ${Services.prefs.getBoolPref( - TrackingProtection.prefEnabled - )}\n`; - body += `${ - TrackingProtection.prefEnabledInPrivateWindows - }: ${Services.prefs.getBoolPref( - TrackingProtection.prefEnabledInPrivateWindows - )}\n`; - body += `urlclassifier.trackingTable: ${Services.prefs.getStringPref( - "urlclassifier.trackingTable" - )}\n`; - body += `network.http.referer.defaultPolicy: ${Services.prefs.getIntPref( - "network.http.referer.defaultPolicy" - )}\n`; - body += `network.http.referer.defaultPolicy.pbmode: ${Services.prefs.getIntPref( - "network.http.referer.defaultPolicy.pbmode" - )}\n`; - body += `${ThirdPartyCookies.prefEnabled}: ${Services.prefs.getIntPref( - ThirdPartyCookies.prefEnabled - )}\n`; - body += `privacy.annotate_channels.strict_list.enabled: ${Services.prefs.getBoolPref( - "privacy.annotate_channels.strict_list.enabled" - )}\n`; - body += `privacy.restrict3rdpartystorage.expiration: ${Services.prefs.getIntPref( - "privacy.restrict3rdpartystorage.expiration" - )}\n`; - body += `${Fingerprinting.prefEnabled}: ${Services.prefs.getBoolPref( - Fingerprinting.prefEnabled - )}\n`; - body += `${Cryptomining.prefEnabled}: ${Services.prefs.getBoolPref( - Cryptomining.prefEnabled - )}\n`; - body += `privacy.globalprivacycontrol.enabled: ${Services.prefs.getBoolPref( - "privacy.globalprivacycontrol.enabled" - )}\n`; - body += `\nhasException: ${this.hasException}\n`; - - body += "\n**Comments**\n" + commentsTextarea.value; - - formData.set("body", body); - - let activatedBlockers = []; - for (let blocker of Object.values(this.blockers)) { - if (blocker.activated) { - activatedBlockers.push(blocker.reportBreakageLabel); - } - } - - formData.set("labels", activatedBlockers.join(",")); - - this._protectionsPopupSendReportButton.disabled = true; - - fetch(reportEndpoint, { - method: "POST", - credentials: "omit", - body: formData, - }) - .then(response => { - this._protectionsPopupSendReportButton.disabled = false; - if (!response.ok) { - console.error( - `Content Blocking report to ${reportEndpoint} failed with status ${response.status}` - ); - this._protectionsPopupSiteNotWorkingReportError.hidden = false; - } else { - this._protectionsPopup.hidePopup(); - ConfirmationHint.show( - this._trackingProtectionIconContainer, - "confirmation-hint-breakage-report-sent" - ); - } - }) - .catch(console.error); - }, - - onSendReportClicked() { - this.submitBreakageReport(this.reportURI); - }, - async maybeUpdateEarliestRecordedDateTooltip(trackerCount) { // If we've already updated or the popup isn't in the DOM yet, don't bother // doing this: diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 8a2a202809..c9ebddb7f5 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -115,6 +115,7 @@ panelview:not([visible]) { flex-shrink: 0; min-width: 0; min-height: 0; + max-height: 100%; } .panel-viewcontainer[panelopen] { @@ -934,82 +935,6 @@ toolbarpaletteitem[place="palette"] > #downloads-button > .toolbarbutton-badge-s display: none; } -@media (-moz-panel-animations) and (prefers-reduced-motion: no-preference) { -@media (-moz-platform: macos) { - /* On Mac, use the properties "-moz-window-transform" and "-moz-window-opacity" - instead of "transform" and "opacity" for these animations. - The -moz-window* properties apply to the whole window including the window's - shadow, and they don't affect the window's "shape", so the system doesn't - have to recompute the shadow shape during the animation. This makes them a - lot faster. In fact, Gecko no longer triggers shadow shape recomputations - for repaints. - These properties are not implemented on other platforms. */ - #BMB_bookmarksPopup:not([animate="false"]) { - transition-property: -moz-window-transform, -moz-window-opacity; - transition-duration: 0.18s, 0.18s; - transition-timing-function: - var(--animation-easing-function), ease-out; - } - - /* Only do the fade-in animation on pre-Big Sur to avoid missing shadows on - * Big Sur, see bug 1672091. */ - @media (-moz-mac-big-sur-theme: 0) { - #BMB_bookmarksPopup:not([animate="false"]) { - -moz-window-opacity: 0; - -moz-window-transform: translateY(-70px); - } - - #BMB_bookmarksPopup[side="bottom"]:not([animate="false"]) { - -moz-window-transform: translateY(70px); - } - } - - /* [animate] is here only so that this rule has greater specificity than the - * rule right above */ - #BMB_bookmarksPopup[animate][animate="open"] { - -moz-window-opacity: 1.0; - transition-duration: 0.18s, 0.18s; - -moz-window-transform: none; - transition-timing-function: - var(--animation-easing-function), ease-in-out; - } - - #BMB_bookmarksPopup[animate][animate="cancel"] { - -moz-window-opacity: 0; - -moz-window-transform: none; - } -} -@media not (-moz-platform: macos) { - #BMB_bookmarksPopup:not([animate="false"]) { - opacity: 0; - transform: translateY(-70px); - transition-property: transform, opacity; - transition-duration: 0.18s, 0.18s; - transition-timing-function: - var(--animation-easing-function), ease-out; - will-change: transform, opacity; - } - - #BMB_bookmarksPopup[side="bottom"]:not([animate="false"]) { - transform: translateY(70px); - } - - /* [animate] is here only so that this rule has greater specificity than the - * rule right above */ - #BMB_bookmarksPopup[animate][animate="open"] { - opacity: 1.0; - transition-duration: 0.18s, 0.18s; - transform: none; - transition-timing-function: - var(--animation-easing-function), ease-in-out; - } - - #BMB_bookmarksPopup[animate][animate="cancel"] { - transform: none; - } -} -} - /* Apply crisp rendering for favicons at exactly 2dppx resolution */ @media (resolution: 2dppx) { .PanelUI-remotetabs-clientcontainer > toolbarbutton > .toolbarbutton-icon, diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 4f145c4949..c91a5d4db2 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -29,7 +29,6 @@ ChromeUtils.defineESModuleGetters(this, { ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.sys.mjs", CustomizableUI: "resource:///modules/CustomizableUI.sys.mjs", - Deprecated: "resource://gre/modules/Deprecated.sys.mjs", DevToolsSocketStatus: "resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs", DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", @@ -185,8 +184,13 @@ XPCOMUtils.defineLazyScriptGetter( ); XPCOMUtils.defineLazyScriptGetter( this, - "TranslationsPanel", - "chrome://browser/content/translations/translationsPanel.js" + "SelectTranslationsPanel", + "chrome://browser/content/translations/selectTranslationsPanel.js" +); +XPCOMUtils.defineLazyScriptGetter( + this, + "FullPageTranslationsPanel", + "chrome://browser/content/translations/fullPageTranslationsPanel.js" ); XPCOMUtils.defineLazyScriptGetter( this, @@ -272,6 +276,11 @@ XPCOMUtils.defineLazyScriptGetter( "gPageStyleMenu", "chrome://browser/content/browser-pagestyle.js" ); +XPCOMUtils.defineLazyScriptGetter( + this, + "gProfiles", + "chrome://browser/content/browser-profiles.js" +); // lazy service getters @@ -524,13 +533,6 @@ XPCOMUtils.defineLazyPreferenceGetter( XPCOMUtils.defineLazyPreferenceGetter( this, - "gBookmarksToolbarShowInPrivate", - "browser.toolbars.bookmarks.showInPrivateBrowsing", - false -); - -XPCOMUtils.defineLazyPreferenceGetter( - this, "gFxaToolbarEnabled", "identity.fxaccounts.toolbar.enabled", false, @@ -720,6 +722,7 @@ var gInitialPages = [ "about:welcome", "about:welcomeback", "chrome://browser/content/blanktab.html", + "about:profilemanager", ]; function isInitialPage(url) { @@ -740,22 +743,13 @@ function browserWindows() { } function updateBookmarkToolbarVisibility() { - // Bug 1846583 - hide bookmarks toolbar in PBM - if ( - gUseFeltPrivacyUI && - !gBookmarksToolbarShowInPrivate && - PrivateBrowsingUtils.isWindowPrivate(window) - ) { - setToolbarVisibility(BookmarkingUI.toolbar, false, false, false); - } else { - BookmarkingUI.updateEmptyToolbarMessage(); - setToolbarVisibility( - BookmarkingUI.toolbar, - gBookmarksToolbarVisibility, - false, - false - ); - } + BookmarkingUI.updateEmptyToolbarMessage(); + setToolbarVisibility( + BookmarkingUI.toolbar, + gBookmarksToolbarVisibility, + false, + false + ); } // This is a stringbundle-like interface to gBrowserBundle, formerly a getter for @@ -1686,13 +1680,13 @@ var gBrowserInit = { gBrowser.addEventListener("DOMUpdateBlockedPopups", gPopupBlockerObserver); gBrowser.addEventListener( "TranslationsParent:LanguageState", - TranslationsPanel + FullPageTranslationsPanel ); gBrowser.addEventListener( "TranslationsParent:OfferTranslation", - TranslationsPanel + FullPageTranslationsPanel ); - gBrowser.addTabsProgressListener(TranslationsPanel); + gBrowser.addTabsProgressListener(FullPageTranslationsPanel); window.addEventListener("AppCommand", HandleAppCommandEvent, true); @@ -1705,12 +1699,10 @@ var gBrowserInit = { if (!gMultiProcessBrowser) { // There is a Content:Click message manually sent from content. - Services.els.addSystemEventListener( - gBrowser.tabpanels, - "click", - contentAreaClick, - true - ); + gBrowser.tabpanels.addEventListener("click", contentAreaClick, { + capture: true, + mozSystemGroup: true, + }); } // hook up UI through progress listener @@ -2424,6 +2416,10 @@ var gBrowserInit = { "browser-idle-startup-tasks-finished" ); }); + + scheduleIdleTask(() => { + gProfiles.init(); + }); }, // Returns the URI(s) to load at startup if it is immediately known, or a @@ -3035,7 +3031,7 @@ function BrowserOpenFileWindow() { }; fp.init( - window, + window.browsingContext, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen ); @@ -3243,15 +3239,6 @@ function BrowserPageInfo( browsingContext, browser ) { - if (HTMLDocument.isInstance(documentURL)) { - Deprecated.warning( - "Please pass the location URL instead of the document " + - "to BrowserPageInfo() as the first argument.", - "https://bugzilla.mozilla.org/show_bug.cgi?id=1238180" - ); - documentURL = documentURL.location; - } - let args = { initialTab, imageElement, browsingContext, browser }; documentURL = documentURL || window.gBrowser.selectedBrowser.currentURI.spec; @@ -4549,7 +4536,23 @@ function toOpenWindowByType(inType, uri, features) { * @return a reference to the new window. */ function OpenBrowserWindow(options = {}) { - return BrowserWindowTracker.openWindow({ openerWindow: window, ...options }); + let telemetryObj = {}; + TelemetryStopwatch.start("FX_NEW_WINDOW_MS", telemetryObj); + + let win = BrowserWindowTracker.openWindow({ + openerWindow: window, + ...options, + }); + + win.addEventListener( + "MozAfterPaint", + () => { + TelemetryStopwatch.finish("FX_NEW_WINDOW_MS", telemetryObj); + }, + { once: true } + ); + + return win; } /** @@ -6456,6 +6459,37 @@ function onViewToolbarsPopupShowing(aEvent, aInsertPoint) { return; } + // 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]; @@ -6509,30 +6543,7 @@ function onViewToolbarsPopupShowing(aEvent, aInsertPoint) { return; } - // triggerNode can be a nested child element of a toolbaritem. - let toolbarItem = popup.triggerNode; - - if (toolbarItem && toolbarItem.localName == "toolbarpaletteitem") { - toolbarItem = toolbarItem.firstElementChild; - } else if (toolbarItem && toolbarItem.localName != "toolbar") { - while (toolbarItem && toolbarItem.parentElement) { - let parent = toolbarItem.parentElement; - if ( - (parent.classList && - 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; - } - } else { - toolbarItem = null; - } - - let showTabStripItems = toolbarItem && toolbarItem.id == "tabbrowser-tabs"; + let showTabStripItems = toolbarItem?.id == "tabbrowser-tabs"; for (let node of popup.querySelectorAll( 'menuitem[contexttype="toolbaritem"]' )) { @@ -6588,9 +6599,7 @@ function onViewToolbarsPopupShowing(aEvent, aInsertPoint) { } let movable = - toolbarItem && - toolbarItem.id && - CustomizableUI.isWidgetRemovable(toolbarItem); + toolbarItem?.id && CustomizableUI.isWidgetRemovable(toolbarItem); if (movable) { if (CustomizableUI.isSpecialWidget(toolbarItem.id)) { moveToPanel.setAttribute("disabled", true); diff --git a/browser/base/content/browser.js.globals b/browser/base/content/browser.js.globals new file mode 100644 index 0000000000..c767bb5beb --- /dev/null +++ b/browser/base/content/browser.js.globals @@ -0,0 +1,305 @@ +[ + "XPCOMUtils", + "AppConstants", + "gBrowser", + "gContextMenu", + "gMultiProcessBrowser", + "gFissionBrowser", + "gBrowserAllowScriptsToCloseInitialTabs", + "gEditUIVisible", + "gReduceMotionSetting", + "gReduceMotionOverride", + "shouldSuppressPopupNotifications", + "gLazyFindCommand", + "gPageIcons", + "gInitialPages", + "isInitialPage", + "browserWindows", + "updateBookmarkToolbarVisibility", + "gNavigatorBundle", + "gScreenshots", + "updateFxaToolbarMenu", + "UpdateBackForwardCommands", + "updatePrintCommands", + "SetClickAndHoldHandlers", + "gClickAndHoldListenersOnElement", + "gSessionHistoryObserver", + "gStoragePressureObserver", + "gPopupBlockerObserver", + "gKeywordURIFixup", + "_createNullPrincipalFromTabUserContextId", + "_resolveDelayedStartup", + "delayedStartupPromise", + "gBrowserInit", + "HandleAppCommandEvent", + "gotoHistoryIndex", + "BrowserForward", + "BrowserBack", + "BrowserHandleBackspace", + "BrowserHandleShiftBackspace", + "BrowserStop", + "BrowserReloadOrDuplicate", + "BrowserReload", + "kSkipCacheFlags", + "BrowserReloadSkipCache", + "BrowserHome", + "loadOneOrMoreURIs", + "openLocation", + "BrowserOpenTab", + "gLastOpenDirectory", + "BrowserOpenFileWindow", + "BrowserCloseTabOrWindow", + "BrowserTryToCloseWindow", + "getLoadContext", + "readFromClipboard", + "BrowserViewSourceOfDocument", + "BrowserViewSource", + "BrowserPageInfo", + "UpdateUrlbarSearchSplitterState", + "UpdatePopupNotificationsVisibility", + "PageProxyClickHandler", + "BrowserOnClick", + "getMeOutOfHere", + "getDefaultHomePage", + "BrowserFullScreen", + "BrowserReloadWithFlags", + "getPEMString", + "browserDragAndDrop", + "homeButtonObserver", + "openHomeDialog", + "newTabButtonObserver", + "newWindowButtonObserver", + "BrowserSearch", + "CreateContainerTabMenu", + "FillHistoryMenu", + "BrowserDownloadsUI", + "toOpenWindowByType", + "OpenBrowserWindow", + "updateEditUIVisibility", + "gFileMenu", + "gShareUtils", + "openNewUserContextTab", + "XULBrowserWindow", + "LinkTargetDisplay", + "CombinedStopReload", + "TabsProgressListener", + "nsBrowserAccess", + "showFullScreenViewContextMenuItems", + "onViewToolbarsPopupShowing", + "onViewToolbarCommand", + "setToolbarVisibility", + "updateToggleControlLabel", + "TabletModeUpdater", + "gTabletModePageCounter", + "displaySecurityInfo", + "gUIDensity", + "nodeToTooltipMap", + "nodeToShortcutMap", + "gDynamicTooltipCache", + "GetDynamicShortcutTooltipText", + "UpdateDynamicShortcutTooltipText", + "hrefAndLinkNodeForClickEvent", + "contentAreaClick", + "handleLinkClick", + "middleMousePaste", + "handleDroppedLink", + "BrowserForceEncodingDetection", + "ToolbarContextMenu", + "BrowserOffline", + "CanvasPermissionPromptHelper", + "WebAuthnPromptHelper", + "CanCloseWindow", + "WindowIsClosing", + "warnAboutClosingWindow", + "MailIntegration", + "BrowserOpenAddonsMgr", + "AddKeywordForSearchField", + "restoreLastClosedTabOrWindowOrSession", + "undoCloseTab", + "undoCloseWindow", + "ReportFalseDeceptiveSite", + "ReportSiteIssue", + "gRemoteControl", + "gPrivateBrowsingUI", + "switchToTabHavingURI", + "RestoreLastSessionObserver", + "MenuTouchModeObserver", + "safeModeRestart", + "duplicateTabIn", + "MousePosTracker", + "ToolbarIconColor", + "PanicButtonNotifier", + "SafeBrowsingNotificationBox", + "TabDialogBox", + "TabModalPromptBox", + "gDialogBox", + "ConfirmationHint", + "FirefoxViewHandler", + "AMTelemetry", + "AboutNewTab", + "AboutReaderParent", + "AddonManager", + "BrowserSearchTelemetry", + "BrowserTelemetryUtils", + "BrowserUIUtils", + "BrowserUsageTelemetry", + "BrowserWindowTracker", + "CFRPageActions", + "Color", + "ContentAnalysis", + "ContextualIdentityService", + "CustomizableUI", + "DevToolsSocketStatus", + "DownloadUtils", + "DownloadsCommon", + "E10SUtils", + "ExtensionsUI", + "FirefoxViewNotificationManager", + "HomePage", + "isProductURL", + "LightweightThemeConsumer", + "LoginHelper", + "LoginManagerParent", + "MigrationUtils", + "NetUtil", + "NewTabPagePreloading", + "NewTabUtils", + "NimbusFeatures", + "OpenInTabsUtils", + "PageActions", + "PageThumbs", + "PanelMultiView", + "PanelView", + "PictureInPicture", + "PlacesTransactions", + "PlacesUIUtils", + "PlacesUtils", + "Pocket", + "PrivateBrowsingUtils", + "ProcessHangMonitor", + "PromptUtils", + "ReaderMode", + "ResetPBMPanel", + "ReportBrokenSite", + "SafeBrowsing", + "Sanitizer", + "SaveToPocket", + "ScreenshotsUtils", + "SearchUIUtils", + "SessionStartup", + "SessionStore", + "ShoppingSidebarParent", + "ShoppingSidebarManager", + "ShortcutUtils", + "SiteDataManager", + "SitePermissions", + "SubDialog", + "SubDialogManager", + "TabCrashHandler", + "TabModalPrompt", + "TabsSetupFlowManager", + "TelemetryEnvironment", + "TranslationsParent", + "UITour", + "UpdateUtils", + "URILoadingHelper", + "UrlbarInput", + "UrlbarPrefs", + "UrlbarProviderSearchTips", + "UrlbarTokenizer", + "UrlbarUtils", + "UrlbarValueFormatter", + "Weave", + "WebNavigationFrames", + "webrtcUI", + "WebsiteFilter", + "ZoomUI", + "fxAccounts", + "PlacesTreeView", + "PlacesInsertionPoint", + "PlacesController", + "PlacesControllerDragHelper", + "PrintUtils", + "ZoomManager", + "FullZoom", + "PanelUI", + "gViewSourceUtils", + "gTabsPanel", + "BrowserAddonUI", + "gExtensionsNotifications", + "gUnifiedExtensions", + "gXPInstallObserver", + "ctrlTab", + "CustomizationHandler", + "AutoHideMenubar", + "PointerLock", + "FullScreen", + "gIdentityHandler", + "gPermissionPanel", + "SelectTranslationsPanel", + "FullPageTranslationsPanel", + "gProtectionsHandler", + "gGestureSupport", + "gHistorySwipeAnimation", + "gSafeBrowsing", + "gSync", + "gBrowserThumbnails", + "openContextMenu", + "nsContextMenu", + "DownloadsPanel", + "DownloadsOverlayLoader", + "DownloadsView", + "DownloadsViewUI", + "DownloadsViewController", + "DownloadsSummary", + "DownloadsFooter", + "DownloadsBlockedSubview", + "DownloadsButton", + "DownloadsIndicatorView", + "gEditItemOverlay", + "gGfxUtils", + "pktUI", + "ToolbarKeyboardNavigator", + "A11yUtils", + "gSharedTabWarning", + "gPageStyleMenu", + "gProfiles", + "ContentPrefService2", + "classifierService", + "Favicons", + "WindowsUIUtils", + "BrowserHandler", + "Marionette", + "RemoteAgent", + "Marionette", + "RemoteAgent", + "RTL_UI", + "gBrandBundle", + "gBrowserBundle", + "gCustomizeMode", + "gNavToolbox", + "gURLBar", + "ReferrerInfo", + "gNotificationBox", + "InlineSpellCheckerUI", + "PopupNotifications", + "MacUserActivityUpdater", + "Win7Features", + "gToolbarKeyNavEnabled", + "gBookmarksToolbarVisibility", + "gFxaToolbarEnabled", + "gFxaToolbarAccessed", + "gAddonAbuseReportEnabled", + "gAlwaysOpenPanel", + "gMiddleClickNewTabUsesPasteboard", + "gScreenshotsDisabled", + "gPrintEnabled", + "gScreenshotsComponentEnabled", + "gTranslationsEnabled", + "gUseFeltPrivacyUI", + "gReduceMotion", + "gFindBar", + "gFindBarInitialized", + "gFindBarPromise", + "BrowserSearch" +] diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml index b4886fb5f7..1dcdd02cd1 100644 --- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -98,6 +98,8 @@ <link rel="localization" href="preview/select-translations.ftl"/> <link rel="localization" href="browser/shopping.ftl"/> <link rel="localization" href="preview/shopping.ftl"/> + <link rel="localization" href="preview/profiles.ftl"/> + <link rel="localization" href="preview/onboarding.ftl"/> <title data-l10n-id="browser-main-window-title"></title> diff --git a/browser/base/content/contentTheme.js b/browser/base/content/contentTheme.js index 805fc778c0..3c46b80bec 100644 --- a/browser/base/content/contentTheme.js +++ b/browser/base/content/contentTheme.js @@ -137,7 +137,7 @@ /** * ContentThemeController handles theme updates sent by the frame script. * To be able to use ContentThemeController, you must add your page to the whitelist - * in LightweightThemeChildListener.jsm + * in LightweightThemeChild.sys.mjs */ const ContentThemeController = { /** diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml index 4074f35ffc..bff8d98b27 100644 --- a/browser/base/content/main-popupset.inc.xhtml +++ b/browser/base/content/main-popupset.inc.xhtml @@ -36,10 +36,10 @@ <menuitem id="context_bookmarkSelectedTabs" hidden="true" data-lazy-l10n-id="bookmark-selected-tabs" - oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueSelectedPages);"/> + oncommand="PlacesCommandHook.bookmarkTabs(gBrowser.selectedTabs);"/> <menuitem id="context_bookmarkTab" data-lazy-l10n-id="tab-context-bookmark-tab" - oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.getUniquePages([TabContextMenu.contextTab]));"/> + oncommand="PlacesCommandHook.bookmarkTabs([TabContextMenu.contextTab]);"/> <menu id="context_moveTabOptions" data-lazy-l10n-id="tab-context-move-tabs" data-l10n-args='{"tabCount": 1}'> @@ -326,11 +326,11 @@ data-lazy-l10n-id="toolbar-context-menu-reload-selected-tabs"/> <menuitem id="toolbar-context-bookmarkSelectedTab" contexttype="tabbar" - oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueSelectedPages);" + oncommand="PlacesCommandHook.bookmarkTabs(gBrowser.selectedTabs);" data-lazy-l10n-id="toolbar-context-menu-bookmark-selected-tab"/> <menuitem id="toolbar-context-bookmarkSelectedTabs" contexttype="tabbar" - oncommand="PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueSelectedPages);" + oncommand="PlacesCommandHook.bookmarkTabs(gBrowser.selectedTabs);" data-lazy-l10n-id="toolbar-context-menu-bookmark-selected-tabs"/> <menuitem id="toolbar-context-selectAllTabs" contexttype="tabbar" @@ -496,7 +496,8 @@ #include ../../components/controlcenter/content/permissionPanel.inc.xhtml #include ../../components/controlcenter/content/protectionsPanel.inc.xhtml #include ../../components/downloads/content/downloadsPanel.inc.xhtml -#include ../../components/translations/content/translationsPanel.inc.xhtml +#include ../../components/translations/content/selectTranslationsPanel.inc.xhtml +#include ../../components/translations/content/fullPageTranslationsPanel.inc.xhtml #include browser-allTabsMenu.inc.xhtml <tooltip id="dynamic-shortcut-tooltip" @@ -624,38 +625,38 @@ </menupopup> <menupopup id="translations-panel-settings-menupopup" - onpopupshown="TranslationsPanel.handleSettingsPopupShownEvent()" - onpopuphidden="TranslationsPanel.handleSettingsPopupHiddenEvent()"> + onpopupshown="FullPageTranslationsPanel.handleSettingsPopupShownEvent()" + onpopuphidden="FullPageTranslationsPanel.handleSettingsPopupHiddenEvent()"> <menuitem class="always-offer-translations-menuitem" data-l10n-id="translations-panel-settings-always-offer-translation" type="checkbox" checked="false" autocheck="false" - oncommand="TranslationsPanel.onAlwaysOfferTranslations()"/> + oncommand="FullPageTranslationsPanel.onAlwaysOfferTranslations()"/> <menuitem class="always-translate-language-menuitem" data-l10n-id="translations-panel-settings-always-translate-unknown-language" type="checkbox" checked="false" autocheck="false" - oncommand="TranslationsPanel.onAlwaysTranslateLanguage()"/> + oncommand="FullPageTranslationsPanel.onAlwaysTranslateLanguage()"/> <menuitem class="never-translate-language-menuitem" data-l10n-id="translations-panel-settings-never-translate-unknown-language" type="checkbox" checked="false" autocheck="false" - oncommand="TranslationsPanel.onNeverTranslateLanguage()"/> + oncommand="FullPageTranslationsPanel.onNeverTranslateLanguage()"/> <menuseparator/> <menuitem class="never-translate-site-menuitem" data-l10n-id="translations-panel-settings-never-translate-site" type="checkbox" checked="false" autocheck="false" - oncommand="TranslationsPanel.onNeverTranslateSite()"/> + oncommand="FullPageTranslationsPanel.onNeverTranslateSite()"/> <menuseparator/> <menuitem class="manage-languages-menuitem" data-l10n-id="translations-panel-settings-manage-languages" - oncommand="TranslationsPanel.openManageLanguages()"/> + oncommand="FullPageTranslationsPanel.openManageLanguages()"/> <menuitem data-l10n-id="translations-panel-settings-about2" - oncommand="TranslationsPanel.onAboutTranslations()"/> + oncommand="FullPageTranslationsPanel.onAboutTranslations()"/> </menupopup> </popupset> diff --git a/browser/base/content/moz.build b/browser/base/content/moz.build index f91487920f..1dd7dccb5f 100644 --- a/browser/base/content/moz.build +++ b/browser/base/content/moz.build @@ -151,6 +151,9 @@ with Files("browser-pageActions.js"): with Files("browser-places.js"): BUG_COMPONENT = ("Firefox", "Bookmarks & History") +with Files("browser-profiles.js"): + BUG_COMPONENT = ("Firefox", "Profiles") + with Files("browser-safebrowsing.js"): BUG_COMPONENT = ("Toolkit", "Safe Browsing") diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml index dce7bacdb3..fc19910726 100644 --- a/browser/base/content/navigator-toolbox.inc.xhtml +++ b/browser/base/content/navigator-toolbox.inc.xhtml @@ -371,8 +371,8 @@ role="button" data-l10n-id="urlbar-translations-button2" hidden="true" - onclick="TranslationsPanel.open(event);" - onkeypress="TranslationsPanel.open(event);"> + onclick="FullPageTranslationsPanel.open(event);" + onkeypress="FullPageTranslationsPanel.open(event);"> <image class="urlbar-icon" id="translations-button-icon" /> <image class="urlbar-icon" id="translations-button-circle-arrows" /> <html:span id="translations-button-locale" aria-hidden="true" /> @@ -610,7 +610,7 @@ oncommand="BookmarkingUI.onCommand(event);"> <menupopup id="BMB_bookmarksPopup" is="places-popup-arrow" - class="toolbar-menupopup" + class="toolbar-menupopup animatable-menupopup" placespopup="true" context="placesContext" openInTabs="children" diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 4e313e7f01..031a32dddf 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -107,6 +107,14 @@ function openContextMenu(aMessage, aBrowser, aActor) { } class nsContextMenu { + /** + * A promise to retrieve the translations language pair + * if the context menu was opened in a context relevant to + * open the SelectTranslationsPanel. + * @type {Promise<{fromLang: string, toLang: string}>} + */ + #translationsLangPairPromise; + constructor(aXulMenu, aIsShift) { // Get contextual info. this.setContext(); @@ -328,7 +336,9 @@ class nsContextMenu { InlineSpellCheckerUI.clearDictionaryListFromMenu(); InlineSpellCheckerUI.uninit(); if ( - Cu.isModuleLoaded("resource://gre/modules/LoginManagerContextMenu.jsm") + Cu.isESModuleLoaded( + "resource://gre/modules/LoginManagerContextMenu.sys.mjs" + ) ) { nsContextMenu.LoginManagerContextMenu.clearLoginsFromMenu(document); } @@ -384,6 +394,11 @@ class nsContextMenu { this.showItem(id, this.inPDFEditor); } + this.showItem( + "context-pdfjs-highlight-selection", + this.pdfEditorStates?.hasSelectedText + ); + if (!this.inPDFEditor) { return; } @@ -1379,9 +1394,7 @@ class nsContextMenu { useGeneratedPassword() { nsContextMenu.LoginManagerContextMenu.useGeneratedPassword( - this.targetIdentifier, - this.contentData.documentURIObject, - this.browser + this.targetIdentifier ); } @@ -2514,9 +2527,65 @@ class nsContextMenu { } /** - * Displays or hides as well as localizes the translate-selection item in the context menu. + * Opens the SelectTranslationsPanel singleton instance. + * + * @param {Event} event - The triggering event for opening the panel. + */ + openSelectTranslationsPanel(event) { + SelectTranslationsPanel.open(event, this.#translationsLangPairPromise); + } + + /** + * Localizes the translate-selection menuitem. + * + * The item will either be localized with a target language's display name + * or localized in a generic way without a target language. + * + * @param {Element} translateSelectionItem + * @returns {Promise<void>} */ - async showTranslateSelectionItem() { + async localizeTranslateSelectionItem(translateSelectionItem) { + const { toLang } = await this.#translationsLangPairPromise; + + if (toLang) { + // A valid to-language exists, so localize the menuitem for that language. + let displayName; + + try { + const displayNames = new Services.intl.DisplayNames(undefined, { + type: "language", + }); + displayName = displayNames.of(toLang); + } catch { + // Services.intl.DisplayNames.of threw, do nothing. + } + + if (displayName) { + document.l10n.setAttributes( + translateSelectionItem, + this.isTextSelected + ? "main-context-menu-translate-selection-to-language" + : "main-context-menu-translate-link-text-to-language", + { language: displayName } + ); + return; + } + } + + // Either no to-language exists, or an error occurred, + // so localize the menuitem without a target language. + document.l10n.setAttributes( + translateSelectionItem, + this.isTextSelected + ? "main-context-menu-translate-selection" + : "main-context-menu-translate-link-text" + ); + } + + /** + * Displays or hides the translate-selection item in the context menu. + */ + showTranslateSelectionItem() { const translateSelectionItem = document.getElementById( "context-translate-selection" ); @@ -2528,7 +2597,7 @@ class nsContextMenu { ); // Selected text takes precedence over link text. - const translatableText = this.isTextSelected + const textToTranslate = this.isTextSelected ? this.selectedText.trim() : this.linkTextStr.trim(); @@ -2536,7 +2605,7 @@ class nsContextMenu { // Only show the item if the feature is enabled. !(translationsEnabled && selectTranslationsEnabled) || // If there is no text to translate, we have nothing to do. - translatableText.length === 0 || + textToTranslate.length === 0 || // We do not allow translating selections on top of Full Page Translations. nsContextMenu.#isFullPageTranslationsActive(); @@ -2544,39 +2613,9 @@ class nsContextMenu { return; } - const preferredLanguages = - nsContextMenu.TranslationsParent.getPreferredLanguages(); - const topPreferredLanguage = preferredLanguages[0]; - - if (topPreferredLanguage) { - const { language } = await nsContextMenu.LanguageDetector.detectLanguage( - translatableText - ); - if (topPreferredLanguage !== language) { - try { - const dn = new Services.intl.DisplayNames(undefined, { - type: "language", - }); - document.l10n.setAttributes( - translateSelectionItem, - this.isTextSelected - ? "main-context-menu-translate-selection-to-language" - : "main-context-menu-translate-link-text-to-language", - { language: dn.of(topPreferredLanguage) } - ); - return; - } catch { - // Services.intl.DisplayNames.of threw, do nothing. - } - } - } - - document.l10n.setAttributes( - translateSelectionItem, - this.isTextSelected - ? "main-context-menu-translate-selection" - : "main-context-menu-translate-link-text" - ); + this.#translationsLangPairPromise = + SelectTranslationsPanel.getLangPairPromise(textToTranslate); + this.localizeTranslateSelectionItem(translateSelectionItem); } // Formats the 'Search <engine> for "<selection or link text>"' context menu. @@ -2681,8 +2720,6 @@ class nsContextMenu { ChromeUtils.defineESModuleGetters(nsContextMenu, { DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.sys.mjs", - LanguageDetector: - "resource://gre/modules/translation/LanguageDetector.sys.mjs", LoginManagerContextMenu: "resource://gre/modules/LoginManagerContextMenu.sys.mjs", TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs", diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js index 178e28ad65..f3999a7cc5 100644 --- a/browser/base/content/pageinfo/pageInfo.js +++ b/browser/base/content/pageinfo/pageInfo.js @@ -686,7 +686,7 @@ async function selectSaveFolder(aCallback) { } }; - fp.init(window, titleText, nsIFilePicker.modeGetFolder); + fp.init(window.browsingContext, titleText, nsIFilePicker.modeGetFolder); fp.appendFilters(nsIFilePicker.filterAll); try { let initialDir = Services.prefs.getComplexValue( diff --git a/browser/base/content/popup-notifications.inc b/browser/base/content/popup-notifications.inc index e554bca50b..daee34e6fe 100644 --- a/browser/base/content/popup-notifications.inc +++ b/browser/base/content/popup-notifications.inc @@ -78,7 +78,7 @@ <popupnotificationcontent orient="vertical"> <label data-l10n-id="panel-save-update-username" control="password-notification-username" class="password-notification-label"></label> <stack> - <html:input id="password-notification-username" + <html:input id="password-notification-username" type="text" class="ac-has-end-icon" autocompletesearch="login-doorhanger-username" @@ -132,7 +132,7 @@ <popupnotificationcontent id="addon-install-blocked-content" orient="vertical"> <description id="addon-install-blocked-message" class="popup-notification-description"></description> <hbox> - <html:a + <html:a is="moz-support-link" id="addon-install-blocked-info" class="popup-notification-learnmore-link" @@ -261,7 +261,7 @@ <popupnotification id="address-save-update-notification" class="address-capture-notification" hidden="true"> <popupnotificationcontent class="address-save-update-notification-content" orient="vertical"> <html:div class="address-capture-header"> - <html:p/> + <html:h1/> </html:div> <html:div class="address-capture-description"> <html:p/> @@ -274,7 +274,7 @@ <popupnotification id="address-edit-notification" class="address-capture-notification" hidden="true"> <popupnotificationcontent class="address-edit-notification-content" orient="vertical"> <html:div class="address-capture-header"> - <html:p/> + <html:h1/> </html:div> <html:div class="address-capture-content"> </html:div> diff --git a/browser/base/content/sanitizeDialog.js b/browser/base/content/sanitizeDialog.js index 2be0d500d5..09a7d927df 100644 --- a/browser/base/content/sanitizeDialog.js +++ b/browser/base/content/sanitizeDialog.js @@ -38,6 +38,14 @@ Preferences.addAll([ { id: "privacy.cpd.siteSettings", type: "bool" }, { id: "privacy.sanitize.timeSpan", type: "int" }, { id: "privacy.clearOnShutdown.history", type: "bool" }, + { id: "privacy.clearHistory.historyFormDataAndDownloads", type: "bool" }, + { id: "privacy.clearHistory.cookiesAndStorage", type: "bool" }, + { id: "privacy.clearHistory.cache", type: "bool" }, + { id: "privacy.clearHistory.siteSettings", type: "bool" }, + { id: "privacy.clearSiteData.historyFormDataAndDownloads", type: "bool" }, + { id: "privacy.clearSiteData.cookiesAndStorage", type: "bool" }, + { id: "privacy.clearSiteData.cache", type: "bool" }, + { id: "privacy.clearSiteData.siteSettings", type: "bool" }, { id: "privacy.clearOnShutdown_v2.historyFormDataAndDownloads", type: "bool", @@ -76,12 +84,6 @@ var gSanitizePromptDialog = { this.siteDataSizes = {}; this.cacheSize = []; - if (!lazy.USE_OLD_DIALOG) { - this._cookiesAndSiteDataCheckbox = - document.getElementById("cookiesAndStorage"); - this._cacheCheckbox = document.getElementById("cache"); - } - let arg = window.arguments?.[0] || {}; // These variables decide which context the dialog has been opened in @@ -93,17 +95,6 @@ var gSanitizePromptDialog = { this._inClearSiteDataNewDialog = arg.mode == "clearSiteData"; } - // Clear site data has it's own default checked boxes, all other entry points - // follow the clear history default prefs - this.defaultCheckedByContext = { - clearHistory: [ - "historyFormDataAndDownloads", - "cookiesAndStorage", - "cache", - ], - clearSiteData: ["cookiesAndStorage", "cache"], - }; - if (arg.inBrowserWindow) { this._dialog.setAttribute("inbrowserwindow", "true"); this._observeTitleForChanges(); @@ -119,7 +110,7 @@ var gSanitizePromptDialog = { if (!lazy.USE_OLD_DIALOG) { // Begin collecting how long it takes to load from here let timerId = Glean.privacySanitize.loadTime.start(); - + this._dataSizesUpdated = false; this.dataSizesFinishedUpdatingPromise = this.getAndUpdateDataSizes() .then(() => { // We're done loading, stop telemetry here @@ -138,50 +129,42 @@ var gSanitizePromptDialog = { let clearPrivateDataGroupbox = document.getElementById( "clearPrivateDataGroupbox" ); + let clearSiteDataGroupbox = document.getElementById( + "clearSiteDataGroupbox" + ); let okButtonl10nID = "sanitize-button-ok"; if (this._inClearOnShutdownNewDialog) { okButtonl10nID = "sanitize-button-ok-on-shutdown"; this._dialog.setAttribute("inClearOnShutdown", "true"); - // remove the clear private data groupbox element - clearPrivateDataGroupbox.remove(); + // remove the other groupbox elements that aren't related to the context + // the dialog is opened in + clearPrivateDataGroupbox.remove(); + clearSiteDataGroupbox.remove(); // If this is the first time the user is opening the new clear on shutdown // dialog, migrate their prefs - Sanitizer.maybeMigrateSanitizeOnShutdownPrefs(); + Sanitizer.maybeMigratePrefs("clearOnShutdown"); } else if (!lazy.USE_OLD_DIALOG) { okButtonl10nID = "sanitize-button-ok2"; - // remove the clear on shutdown groupbox element clearOnShutdownGroupbox.remove(); - } - document.l10n.setAttributes(OKButton, okButtonl10nID); - - // update initial checkbox values based on the context the dialog is opened - // from (history, site data). Categories are not remembered - // from the last time the dialog was used. - if (!lazy.USE_OLD_DIALOG && !this._inClearOnShutdownNewDialog) { - let defaults = this.defaultCheckedByContext.clearHistory; if (this._inClearSiteDataNewDialog) { - defaults = this.defaultCheckedByContext.clearSiteData; + clearPrivateDataGroupbox.remove(); + // we do not need to migrate prefs for clear site data, + // since we decided to keep the default options for + // privacy.clearSiteData.* to stay consistent with old behaviour + // of the clear site data dialog box + } else { + clearSiteDataGroupbox.remove(); + Sanitizer.maybeMigratePrefs("cpd"); } + } + document.l10n.setAttributes(OKButton, okButtonl10nID); - this._allCheckboxes = document.querySelectorAll( - "#clearPrivateDataGroupbox .clearingItemCheckbox" - ); - this._allCheckboxes.forEach(checkbox => { - let pref = checkbox.id; - let value = false; - if (defaults.includes(pref)) { - value = true; - checkbox.checked = value; - } - - // Add event listeners to the checkboxes to ensure that the clear button is - // disabled if no checkboxes are checked - checkbox.addEventListener("command", _ => - this.updateAcceptButtonState() - ); - }); + if (!lazy.USE_OLD_DIALOG) { + this._cookiesAndSiteDataCheckbox = + document.getElementById("cookiesAndStorage"); + this._cacheCheckbox = document.getElementById("cache"); } document.addEventListener("dialogaccept", e => { @@ -191,10 +174,13 @@ var gSanitizePromptDialog = { if (!lazy.USE_OLD_DIALOG) { this.reportTelemetry("clear"); } + this.sanitize(e); } }); + this._allCheckboxes = document.querySelectorAll("checkbox[preference]"); + this.registerSyncFromPrefListeners(); // we want to show the warning box for all cases except clear on shutdown @@ -222,8 +208,6 @@ var gSanitizePromptDialog = { if (!lazy.USE_OLD_DIALOG) { this.reportTelemetry("open"); } - - await this.dataSizesFinishedUpdatingPromise; }, updateAcceptButtonState() { @@ -306,6 +290,7 @@ var gSanitizePromptDialog = { ignoreTimespan: !range, range, }; + let itemsToClear = this.getItemsToClear(); Sanitizer.sanitize(itemsToClear, options) .catch(console.error) @@ -347,14 +332,9 @@ var gSanitizePromptDialog = { * Return the boolean prefs that correspond to the checkboxes on the dialog. */ _getItemPrefs() { - return Preferences.getAll().filter(pref => { - // The timespan pref isn't a bool, so don't return it - if (pref.id == "privacy.sanitize.timeSpan") { - return false; - } - // In the old dialog, cpd.downloads isn't controlled by a checkbox - return !(lazy.USE_OLD_DIALOG && pref.id == "privacy.cpd.downloads"); - }); + return Array.from(this._allCheckboxes).map(checkbox => + checkbox.getAttribute("preference") + ); }, /** @@ -367,7 +347,7 @@ var gSanitizePromptDialog = { // and (in the old dialog) privacy.cpd.downloads which is not controlled // directly by a checkbox). var found = this._getItemPrefs().some( - pref => !!pref.value && !pref.disabled + pref => Preferences.get(pref).value === true ); try { @@ -417,6 +397,8 @@ var gSanitizePromptDialog = { ); } this.cacheSize = lazy.DownloadUtils.convertByteUnits(cacheSize); + + this._dataSizesUpdated = true; this.updateDataSizesInUI(); }, @@ -441,7 +423,7 @@ var gSanitizePromptDialog = { // elements. var prefs = this._getItemPrefs(); for (let i = 0; i < prefs.length; ++i) { - var p = prefs[i]; + var p = Preferences.get(prefs[i]); Services.prefs.setBoolPref(p.id, p.value); } }, @@ -492,6 +474,10 @@ var gSanitizePromptDialog = { * Updates data sizes displayed based on new selected timespan */ updateDataSizesInUI() { + if (!this._dataSizesUpdated) { + return; + } + const TIMESPAN_SELECTION_MAP = { 0: "TIMESPAN_EVERYTHING", 1: "TIMESPAN_HOUR", @@ -531,11 +517,7 @@ var gSanitizePromptDialog = { } let items = []; - let clearPrivateDataGroupbox = document.getElementById( - "clearPrivateDataGroupbox" - ); - - for (let cb of clearPrivateDataGroupbox.querySelectorAll("checkbox")) { + for (let cb of this._allCheckboxes) { if (cb.checked) { items.push(cb.id); } diff --git a/browser/base/content/sanitize_v2.xhtml b/browser/base/content/sanitize_v2.xhtml index 97ad484e5f..4d74e140a3 100644 --- a/browser/base/content/sanitize_v2.xhtml +++ b/browser/base/content/sanitize_v2.xhtml @@ -108,6 +108,7 @@ <checkbox class="clearingItemCheckbox" data-l10n-id="item-history-form-data-downloads" + preference="privacy.clearHistory.historyFormDataAndDownloads" id="historyFormDataAndDownloads" /> <description @@ -123,6 +124,7 @@ class="clearingItemCheckbox" data-l10n-id="item-cookies-site-data" aria-describedby="cookies-site-data-description" + preference="privacy.clearHistory.cookiesAndStorage" id="cookiesAndStorage" /> <description @@ -138,6 +140,7 @@ class="clearingItemCheckbox" data-l10n-id="item-cached-content" aria-describedby="cached-content-description" + preference="privacy.clearHistory.cache" id="cache" /> <description @@ -153,6 +156,72 @@ class="clearingItemCheckbox" data-l10n-id="item-site-prefs" aria-describedby="site-prefs-description" + preference="privacy.clearHistory.siteSettings" + id="siteSettings" + /> + <description + id="site-prefs-description" + data-l10n-id="item-site-prefs-description" + class="sanitizeCheckboxDescription text-deemphasized" + /> + </vbox> + </hbox> + </groupbox> + <groupbox id="clearSiteDataGroupbox"> + <hbox class="checkboxWithDescription"> + <vbox> + <checkbox + class="clearingItemCheckbox" + data-l10n-id="item-history-form-data-downloads" + preference="privacy.clearSiteData.historyFormDataAndDownloads" + id="historyFormDataAndDownloads" + /> + <description + id="history-form-data-description" + data-l10n-id="item-history-form-data-downloads-description" + class="sanitizeCheckboxDescription text-deemphasized" + /> + </vbox> + </hbox> + <hbox class="checkboxWithDescription"> + <vbox> + <checkbox + class="clearingItemCheckbox" + data-l10n-id="item-cookies-site-data" + aria-describedby="cookies-site-data-description" + preference="privacy.clearSiteData.cookiesAndStorage" + id="cookiesAndStorage" + /> + <description + id="cookies-site-data-description" + data-l10n-id="item-cookies-site-data-description" + class="sanitizeCheckboxDescription text-deemphasized" + /> + </vbox> + </hbox> + <hbox class="checkboxWithDescription"> + <vbox> + <checkbox + class="clearingItemCheckbox" + data-l10n-id="item-cached-content" + aria-describedby="cached-content-description" + preference="privacy.clearSiteData.cache" + id="cache" + /> + <description + id="cached-content-description" + data-l10n-id="item-cached-content-description" + class="sanitizeCheckboxDescription text-deemphasized" + /> + </vbox> + </hbox> + <hbox class="checkboxWithDescription"> + <vbox> + <checkbox + class="clearingItemCheckbox" + data-l10n-id="item-site-prefs" + aria-describedby="site-prefs-description" + preference="privacy.clearSiteData.siteSettings" id="siteSettings" /> <description diff --git a/browser/base/content/tabbrowser-tab.js b/browser/base/content/tabbrowser-tab.js index 01bb7ee90b..ed3d4bb727 100644 --- a/browser/base/content/tabbrowser-tab.js +++ b/browser/base/content/tabbrowser-tab.js @@ -350,7 +350,20 @@ count: affectedTabsLength, }); } - this._mouseenter(); + + if (this.hidden || this.closing) { + return; + } + + let tabToWarm = this.mOverCloseButton + ? gBrowser._findTabToBlurTo(this) + : this; + gBrowser.warmupTab(tabToWarm); + + // If the previous target wasn't part of this tab then this is a mouseenter event. + if (!this.contains(event.relatedTarget)) { + this._mouseenter(); + } } on_mouseout(event) { @@ -360,7 +373,11 @@ if (event.target == this.overlayIcon) { this.setSecondaryTabTooltipLabel(null); } - this._mouseleave(); + + // If the new target is not part of this tab then this is a mouseleave event. + if (!this.contains(event.relatedTarget)) { + this._mouseleave(); + } } on_dragstart(event) { @@ -530,9 +547,6 @@ } _mouseenter() { - if (this.hidden || this.closing) { - return; - } this._hover = true; if (this.selected) { @@ -545,12 +559,6 @@ // Prepare connection to host beforehand. SessionStore.speculativeConnectOnTabHover(this); - let tabToWarm = this; - if (this.mOverCloseButton) { - tabToWarm = gBrowser._findTabToBlurTo(this); - } - gBrowser.warmupTab(tabToWarm); - this.dispatchEvent(new CustomEvent("TabHoverStart", { bubbles: true })); } diff --git a/browser/base/content/tabbrowser-tabs.js b/browser/base/content/tabbrowser-tabs.js index 4733e6d0b6..36b6aeb390 100644 --- a/browser/base/content/tabbrowser-tabs.js +++ b/browser/base/content/tabbrowser-tabs.js @@ -187,6 +187,26 @@ this.updateTabIndicatorAttr(event.target); } + on_TabHoverStart(event) { + if (this._showCardPreviews) { + const previewContainer = document.getElementById( + "tabbrowser-tab-preview" + ); + previewContainer.tab = event.target; + } + } + + on_TabHoverEnd(event) { + if (this._showCardPreviews) { + const previewContainer = document.getElementById( + "tabbrowser-tab-preview" + ); + if (previewContainer.tab === event.target) { + previewContainer.tab = null; + } + } + } + on_transitionend(event) { if (event.propertyName != "max-width") { return; @@ -1818,22 +1838,6 @@ handleEvent(aEvent) { switch (aEvent.type) { - case "TabHoverStart": - if (this._showCardPreviews) { - const previewContainer = document.getElementById( - "tabbrowser-tab-preview" - ); - previewContainer.tab = aEvent.target; - } - break; - case "TabHoverEnd": - if (this._showCardPreviews) { - const previewContainer = document.getElementById( - "tabbrowser-tab-preview" - ); - previewContainer.tab = null; - } - break; case "mouseout": // If the "related target" (the node to which the pointer went) is not // a child of the current document, the mouse just left the window. diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js index a9637ad262..54a801939a 100644 --- a/browser/base/content/tabbrowser.js +++ b/browser/base/content/tabbrowser.js @@ -119,8 +119,8 @@ Services.obs.addObserver(this, "contextual-identity-updated"); - Services.els.addSystemEventListener(document, "keydown", this, false); - Services.els.addSystemEventListener(document, "keypress", this, false); + document.addEventListener("keydown", this, { mozSystemGroup: true }); + document.addEventListener("keypress", this, { mozSystemGroup: true }); document.addEventListener("visibilitychange", this); window.addEventListener("framefocusrequested", this); window.addEventListener("activate", this); @@ -5948,14 +5948,11 @@ } } - Services.els.removeSystemEventListener(document, "keydown", this, false); + document.removeEventListener("keydown", this, { mozSystemGroup: true }); if (AppConstants.platform == "macosx") { - Services.els.removeSystemEventListener( - document, - "keypress", - this, - false - ); + document.removeEventListener("keypress", this, { + mozSystemGroup: true, + }); } document.removeEventListener("visibilitychange", this); window.removeEventListener("framefocusrequested", this); diff --git a/browser/base/content/test/about/browser.toml b/browser/base/content/test/about/browser.toml index 900c6b7140..98961200a0 100644 --- a/browser/base/content/test/about/browser.toml +++ b/browser/base/content/test/about/browser.toml @@ -34,7 +34,8 @@ support-files = [ ["browser_aboutHome_search_suggestion.js"] skip-if = [ "os == 'mac'", - "os == 'linux' && (!debug || bits == 64)", + "os == 'linux' && os_version == '18.04' && !debug", + "os == 'linux' && os_version == '18.04' && bits == 64", "win10_2009 && bits == 64 && !debug", # Bug 1399648, bug 1402502 ] @@ -75,7 +76,7 @@ skip-if = ["tsan"] # Bug 1676326, highly frequent on TSan ["browser_aboutStopReload.js"] ["browser_aboutSupport.js"] -skip-if = ["os == 'linux' && bits == 64 && asan && !debug"] # Bug 1713368 +skip-if = ["os == 'linux' && os_version == '18.04' && asan"] # Bug 1713368 ["browser_aboutSupport_newtab_security_state.js"] @@ -83,6 +84,9 @@ skip-if = ["os == 'linux' && bits == 64 && asan && !debug"] # Bug 1713368 skip-if = ["os == 'android'"] ["browser_bug435325.js"] -skip-if = ["verify && !debug && os == 'mac'"] +skip-if = [ + "apple_catalina && !debug && verify", + "apple_silicon && !debug && verify", +] ["browser_bug633691.js"] diff --git a/browser/base/content/test/about/browser_aboutNetError.js b/browser/base/content/test/about/browser_aboutNetError.js index 8222a0f6e8..9131f5e696 100644 --- a/browser/base/content/test/about/browser_aboutNetError.js +++ b/browser/base/content/test/about/browser_aboutNetError.js @@ -25,6 +25,38 @@ function resetPrefs() { Services.prefs.clearUserPref("browser.fixup.alternate.enabled"); } +async function resetTelemetry() { + Services.telemetry.clearEvents(); + await TestUtils.waitForCondition(() => { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).content; + return !events || !events.length; + }); + Services.telemetry.setEventRecordingEnabled("security.ui.tlserror", true); +} + +async function checkTelemetry(errorString) { + let loadEvent = await TestUtils.waitForCondition(() => { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, + true + ).content; + return events?.find(e => e[1] == "security.ui.tlserror" && e[2] == "load"); + }, "recorded telemetry for the load"); + loadEvent.shift(); + Assert.deepEqual(loadEvent, [ + "security.ui.tlserror", + "load", + "abouttlserror", + errorString, + { + is_frame: "false", + }, + ]); +} + add_task(async function resetToDefaultConfig() { info( "Change TLS config to cause page load to fail, check that reset button is shown and that it works" @@ -34,6 +66,8 @@ add_task(async function resetToDefaultConfig() { Services.prefs.setIntPref("security.tls.version.min", 1); // TLS 1.0 Services.prefs.setIntPref("security.tls.version.max", 1); + await resetTelemetry(); + let browser; let pageLoaded; await BrowserTestUtils.openNewForegroundTab( @@ -49,6 +83,8 @@ add_task(async function resetToDefaultConfig() { info("Loading and waiting for the net error"); await pageLoaded; + await checkTelemetry("SSL_ERROR_PROTOCOL_VERSION_ALERT"); + // Setup an observer for the target page. const finalLoadComplete = BrowserTestUtils.browserLoaded( browser, @@ -92,6 +128,8 @@ add_task(async function checkLearnMoreLink() { Services.prefs.setIntPref("security.tls.version.min", 3); Services.prefs.setIntPref("security.tls.version.max", 4); + await resetTelemetry(); + let browser; let pageLoaded; await BrowserTestUtils.openNewForegroundTab( @@ -107,6 +145,8 @@ add_task(async function checkLearnMoreLink() { info("Loading and waiting for the net error"); await pageLoaded; + await checkTelemetry("SSL_ERROR_PROTOCOL_VERSION_ALERT"); + const baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); await SpecialPowers.spawn(browser, [baseURL], function (_baseURL) { @@ -206,6 +246,8 @@ add_task(async function checkDomainCorrection() { // Test that ciphersuites that use 3DES (namely, TLS_RSA_WITH_3DES_EDE_CBC_SHA) // can only be enabled when deprecated TLS is enabled. add_task(async function onlyAllow3DESWithDeprecatedTLS() { + await resetTelemetry(); + // By default, connecting to a server that only uses 3DES should fail. await BrowserTestUtils.withNewTab( { gBrowser, url: "about:blank" }, @@ -215,6 +257,8 @@ add_task(async function onlyAllow3DESWithDeprecatedTLS() { } ); + await checkTelemetry("SSL_ERROR_NO_CYPHER_OVERLAP"); + // Enabling deprecated TLS should also enable 3DES. Services.prefs.setBoolPref("security.tls.version.enable-deprecated", true); await BrowserTestUtils.withNewTab( diff --git a/browser/base/content/test/contextMenu/browser_bug1798178.js b/browser/base/content/test/contextMenu/browser_bug1798178.js index 529665a6f9..de8bab1820 100644 --- a/browser/base/content/test/contextMenu/browser_bug1798178.js +++ b/browser/base/content/test/contextMenu/browser_bug1798178.js @@ -15,7 +15,7 @@ const TEST_URL = ) + "file_bug1798178.html"; let MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); function createTemporarySaveDirectory() { let saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); diff --git a/browser/base/content/test/contextMenu/browser_contextmenu_save_blocked.js b/browser/base/content/test/contextMenu/browser_contextmenu_save_blocked.js index 5064d9a316..062fbeac08 100644 --- a/browser/base/content/test/contextMenu/browser_contextmenu_save_blocked.js +++ b/browser/base/content/test/contextMenu/browser_contextmenu_save_blocked.js @@ -4,7 +4,7 @@ "use strict"; var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); function mockPromptService() { let { prompt } = Services; diff --git a/browser/base/content/test/forms/browser.toml b/browser/base/content/test/forms/browser.toml index 33d73ba8bf..95b666369e 100644 --- a/browser/base/content/test/forms/browser.toml +++ b/browser/base/content/test/forms/browser.toml @@ -1,5 +1,5 @@ [DEFAULT] -prefs = ["gfx.font_loader.delay=0", "dom.select.showPicker.enabled=true"] +prefs = ["gfx.font_loader.delay=0", "dom.select.showPicker.enabled=true", "font.minimum-size.x-western=9"] support-files = ["head.js"] ["browser_selectpopup.js"] @@ -18,6 +18,8 @@ skip-if = ["os == 'linux'"] # Bug 1329991 - test fails intermittently on Linux b ["browser_selectpopup_large.js"] +["browser_selectpopup_minFontSize.js"] + ["browser_selectpopup_searchfocus.js"] fail-if = ["a11y_checks"] # Bug 1854233 input may not be labeled diff --git a/browser/base/content/test/forms/browser_selectpopup_minFontSize.js b/browser/base/content/test/forms/browser_selectpopup_minFontSize.js new file mode 100644 index 0000000000..d240c2d2d0 --- /dev/null +++ b/browser/base/content/test/forms/browser_selectpopup_minFontSize.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// NOTE that this test expects "font.minimum-size.x-western=9" to be set +// in the manifest. + +const PAGE = ` +<!doctype html> +<body lang="en-US"> +<select> + <option style="font-size:24px">A</option> + <option style="font-size:6px">BCD</option> +</select> +`; + +add_task(async function () { + const url = "data:text/html," + encodeURI(PAGE); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url, + }, + async function (browser) { + let popup = await openSelectPopup("click"); + let menuitems = popup.querySelectorAll("menuitem"); + is( + getComputedStyle(menuitems[0]).fontSize, + "24px", + "font-size should be honored" + ); + is( + getComputedStyle(menuitems[1]).fontSize, + "9px", + "minimum font-size should be honored" + ); + } + ); +}); diff --git a/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js index 8a77f01ce4..8eb07a863a 100644 --- a/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js +++ b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js @@ -1,36 +1,11 @@ const TEST_PAGE = "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html"; -const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( - "prompts.contentPromptSubDialog", - false -); - var expectingDialog = false; var wantToClose = true; var resolveDialogPromise; -function onTabModalDialogLoaded(node) { - ok( - !CONTENT_PROMPT_SUBDIALOG, - "Should not be using content prompt subdialogs." - ); - ok(expectingDialog, "Should be expecting this dialog."); - expectingDialog = false; - if (wantToClose) { - // This accepts the dialog, closing it - node.querySelector(".tabmodalprompt-button0").click(); - } else { - // This keeps the page open - node.querySelector(".tabmodalprompt-button1").click(); - } - if (resolveDialogPromise) { - resolveDialogPromise(); - } -} - function onCommonDialogLoaded(promptWindow) { - ok(CONTENT_PROMPT_SUBDIALOG, "Should be using content prompt subdialogs."); ok(expectingDialog, "Should be expecting this dialog."); expectingDialog = false; let dialog = promptWindow.Dialog; @@ -51,11 +26,9 @@ SpecialPowers.pushPrefEnv({ }); // Listen for the dialog being created -Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded"); Services.obs.addObserver(onCommonDialogLoaded, "common-dialog-loaded"); registerCleanupFunction(() => { Services.prefs.clearUserPref("browser.tabs.warnOnClose"); - Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded"); Services.obs.removeObserver(onCommonDialogLoaded, "common-dialog-loaded"); }); diff --git a/browser/base/content/test/general/browser_bug676619.js b/browser/base/content/test/general/browser_bug676619.js index 24d8d88447..80bbce8cb0 100644 --- a/browser/base/content/test/general/browser_bug676619.js +++ b/browser/base/content/test/general/browser_bug676619.js @@ -1,5 +1,5 @@ var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); function waitForNewWindow() { return new Promise(resolve => { diff --git a/browser/base/content/test/general/browser_double_close_tab.js b/browser/base/content/test/general/browser_double_close_tab.js index 554aeb8077..f5f2f1b6c7 100644 --- a/browser/base/content/test/general/browser_double_close_tab.js +++ b/browser/base/content/test/general/browser_double_close_tab.js @@ -4,24 +4,15 @@ const TEST_PAGE = "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html"; var testTab; -const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( - "prompts.contentPromptSubDialog", - false -); - function waitForDialog(callback) { function onDialogLoaded(nodeOrDialogWindow) { - let node = CONTENT_PROMPT_SUBDIALOG - ? nodeOrDialogWindow.document.querySelector("dialog") - : nodeOrDialogWindow; - Services.obs.removeObserver(onDialogLoaded, "tabmodal-dialog-loaded"); + let node = nodeOrDialogWindow.document.querySelector("dialog"); Services.obs.removeObserver(onDialogLoaded, "common-dialog-loaded"); // Allow dialog's onLoad call to run to completion Promise.resolve().then(() => callback(node)); } // Listen for the dialog being created - Services.obs.addObserver(onDialogLoaded, "tabmodal-dialog-loaded"); Services.obs.addObserver(onDialogLoaded, "common-dialog-loaded"); } @@ -35,9 +26,7 @@ function waitForDialogDestroyed(node, callback) { }); observer.observe(node.parentNode, { childList: true }); - if (CONTENT_PROMPT_SUBDIALOG) { - node.ownerGlobal.addEventListener("unload", done); - } + node.ownerGlobal.addEventListener("unload", done); let failureTimeout = setTimeout(function () { ok(false, "Dialog should have been destroyed"); @@ -49,12 +38,8 @@ function waitForDialogDestroyed(node, callback) { observer.disconnect(); observer = null; - if (CONTENT_PROMPT_SUBDIALOG) { - node.ownerGlobal.removeEventListener("unload", done); - SimpleTest.executeSoon(callback); - } else { - callback(); - } + node.ownerGlobal.removeEventListener("unload", done); + SimpleTest.executeSoon(callback); } } @@ -76,23 +61,12 @@ add_task(async function () { let doCompletion = () => setTimeout(resolveOuter, 0); info("Now checking if dialog is destroyed"); - if (CONTENT_PROMPT_SUBDIALOG) { - ok( - !dialogNode.ownerGlobal || dialogNode.ownerGlobal.closed, - "onbeforeunload dialog should be gone." - ); - if (dialogNode.ownerGlobal && !dialogNode.ownerGlobal.closed) { - dialogNode.acceptDialog(); - } - } else { - ok(!dialogNode.parentNode, "onbeforeunload dialog should be gone."); - if (dialogNode.parentNode) { - // Failed to remove onbeforeunload dialog, so do it ourselves: - let leaveBtn = dialogNode.querySelector(".tabmodalprompt-button0"); - waitForDialogDestroyed(dialogNode, doCompletion); - EventUtils.synthesizeMouseAtCenter(leaveBtn, {}); - return; - } + ok( + !dialogNode.ownerGlobal || dialogNode.ownerGlobal.closed, + "onbeforeunload dialog should be gone." + ); + if (dialogNode.ownerGlobal && !dialogNode.ownerGlobal.closed) { + dialogNode.acceptDialog(); } doCompletion(); diff --git a/browser/base/content/test/general/browser_minimize.js b/browser/base/content/test/general/browser_minimize.js index a57fea079c..3919cd7d77 100644 --- a/browser/base/content/test/general/browser_minimize.js +++ b/browser/base/content/test/general/browser_minimize.js @@ -12,26 +12,57 @@ add_task(async function () { ok(isActive(), "Docshell should be active when starting the test"); ok(!document.hidden, "Top level window should be visible"); + // When we show or hide the window (including by minimization), + // there are 2 signifiers that the process is complete: the + // sizemodechange event, and the browsing context becoming active + // or inactive. There is another signifier, the + // occlusionstatechange event, but whether or not that event + // is sent is platform-dependent, so it's not very useful. The + // safest way to check for stable state is to build promises + // around sizemodechange and browsing context active and then + // wait for them all to complete, and that's what we do here. info("Calling window.minimize"); let promiseSizeModeChange = BrowserTestUtils.waitForEvent( window, "sizemodechange" + ).then( + () => ok(true, "Got sizemodechange."), + () => ok(false, "Rejected sizemodechange.") + ); + let promiseBrowserInactive = BrowserTestUtils.waitForCondition( + () => !isActive(), + "Docshell should be inactive." + ).then( + () => ok(true, "Got inactive."), + () => ok(false, "Rejected inactive.") ); window.minimize(); - await promiseSizeModeChange; - ok(!isActive(), "Docshell should be Inactive"); + await Promise.all([promiseSizeModeChange, promiseBrowserInactive]); ok(document.hidden, "Top level window should be hidden"); + // When we restore the window from minimization, we have the + // same concerns as above, so prepare our promises. info("Calling window.restore"); promiseSizeModeChange = BrowserTestUtils.waitForEvent( window, "sizemodechange" + ).then( + () => ok(true, "Got sizemodechange."), + () => ok(false, "Rejected sizemodechange.") + ); + let promiseBrowserActive = BrowserTestUtils.waitForCondition( + () => isActive(), + "Docshell should be active." + ).then( + () => ok(true, "Got active."), + () => ok(false, "Rejected active.") ); window.restore(); + // On Ubuntu `window.restore` doesn't seem to work, use a timer to make the // test fail faster and more cleanly than with a test timeout. await Promise.race([ - promiseSizeModeChange, + Promise.all([promiseSizeModeChange, promiseBrowserActive]), new Promise((resolve, reject) => // eslint-disable-next-line mozilla/no-arbitrary-setTimeout setTimeout(() => { @@ -39,11 +70,5 @@ add_task(async function () { }, 5000) ), ]); - // The sizemodechange event can sometimes be fired before the - // occlusionstatechange event, especially in chaos mode. - if (window.isFullyOccluded) { - await BrowserTestUtils.waitForEvent(window, "occlusionstatechange"); - } - ok(isActive(), "Docshell should be active again"); ok(!document.hidden, "Top level window should be visible"); }); diff --git a/browser/base/content/test/general/browser_save_link-perwindowpb.js b/browser/base/content/test/general/browser_save_link-perwindowpb.js index b018212280..234813ca2c 100644 --- a/browser/base/content/test/general/browser_save_link-perwindowpb.js +++ b/browser/base/content/test/general/browser_save_link-perwindowpb.js @@ -2,7 +2,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); // Trigger a save of a link in public mode, then trigger an identical save // in private mode and ensure that the second request is differentiated from @@ -14,7 +14,7 @@ function triggerSave(aWindow, aCallback) { let testBrowser = aWindow.gBrowser.selectedBrowser; // This page sets a cookie if and only if a cookie does not exist yet let testURI = - "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517-2.html"; + "https://example.com/browser/browser/base/content/test/general/bug792517-2.html"; BrowserTestUtils.startLoadingURIString(testBrowser, testURI); BrowserTestUtils.browserLoaded(testBrowser, false, testURI).then(() => { waitForFocus(function () { @@ -132,7 +132,7 @@ function test() { info("onExamineResponse with " + channel.URI.spec); if ( channel.URI.spec != - "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs" + "https://example.com/browser/browser/base/content/test/general/bug792517.sjs" ) { info("returning"); return; @@ -158,7 +158,7 @@ function test() { info("onModifyRequest with " + channel.URI.spec); if ( channel.URI.spec != - "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs" + "https://example.com/browser/browser/base/content/test/general/bug792517.sjs" ) { return; } diff --git a/browser/base/content/test/general/browser_save_link_when_window_navigates.js b/browser/base/content/test/general/browser_save_link_when_window_navigates.js index e7507fcbb0..65daef5f1b 100644 --- a/browser/base/content/test/general/browser_save_link_when_window_navigates.js +++ b/browser/base/content/test/general/browser_save_link_when_window_navigates.js @@ -2,7 +2,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); const SAVE_PER_SITE_PREF = "browser.download.lastDir.savePerSite"; const ALWAYS_DOWNLOAD_DIR_PREF = "browser.download.useDownloadDir"; @@ -36,7 +36,7 @@ function triggerSave(aWindow, aCallback) { var fileName; let testBrowser = aWindow.gBrowser.selectedBrowser; let testURI = - "http://mochi.test:8888/browser/browser/base/content/test/general/navigating_window_with_download.html"; + "https://example.com/browser/browser/base/content/test/general/navigating_window_with_download.html"; // Only observe the UTC dialog if it's enabled by pref if (Services.prefs.getBoolPref(ALWAYS_ASK_PREF)) { diff --git a/browser/base/content/test/general/browser_save_private_link_perwindowpb.js b/browser/base/content/test/general/browser_save_private_link_perwindowpb.js index 8ede97e640..42632bdc5a 100644 --- a/browser/base/content/test/general/browser_save_private_link_perwindowpb.js +++ b/browser/base/content/test/general/browser_save_private_link_perwindowpb.js @@ -43,7 +43,7 @@ function promiseImageDownloaded() { return new Promise((resolve, reject) => { let fileName; let MockFilePicker = SpecialPowers.MockFilePicker; - MockFilePicker.init(window); + MockFilePicker.init(window.browsingContext); function onTransferComplete(downloadSuccess) { ok( diff --git a/browser/base/content/test/general/browser_save_video.js b/browser/base/content/test/general/browser_save_video.js index 276088fbb1..e9701d7023 100644 --- a/browser/base/content/test/general/browser_save_video.js +++ b/browser/base/content/test/general/browser_save_video.js @@ -2,7 +2,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); /** * TestCase for bug 564387 @@ -14,7 +14,7 @@ add_task(async function () { let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); BrowserTestUtils.startLoadingURIString( gBrowser, - "http://mochi.test:8888/browser/browser/base/content/test/general/web_video.html" + "https://example.com/browser/browser/base/content/test/general/web_video.html" ); await loadPromise; diff --git a/browser/base/content/test/general/browser_save_video_frame.js b/browser/base/content/test/general/browser_save_video_frame.js index 877c33bcd3..11fe3ac80e 100644 --- a/browser/base/content/test/general/browser_save_video_frame.js +++ b/browser/base/content/test/general/browser_save_video_frame.js @@ -49,7 +49,7 @@ function waitForTransferComplete() { */ add_task(async function () { let MockFilePicker = SpecialPowers.MockFilePicker; - MockFilePicker.init(window); + MockFilePicker.init(window.browsingContext); // Create the folder the video will be saved into. let destDir = createTemporarySaveDirectory(); diff --git a/browser/base/content/test/general/browser_visibleTabs_bookmarkAllPages.js b/browser/base/content/test/general/browser_visibleTabs_bookmarkAllPages.js index 2c0002fc44..2ec3b632f2 100644 --- a/browser/base/content/test/general/browser_visibleTabs_bookmarkAllPages.js +++ b/browser/base/content/test/general/browser_visibleTabs_bookmarkAllPages.js @@ -15,7 +15,9 @@ function test() { is(gBrowser.visibleTabs.length, 1, "Only one tab is visible"); - let uris = PlacesCommandHook.uniqueCurrentPages; + let uris = PlacesCommandHook.getUniquePages( + gBrowser.visibleTabs.filter(tab => !tab.pinned) + ); is(uris.length, 1, "Only one uri is returned"); is( diff --git a/browser/base/content/test/general/navigating_window_with_download.html b/browser/base/content/test/general/navigating_window_with_download.html index 6b0918941f..8649168cf5 100644 --- a/browser/base/content/test/general/navigating_window_with_download.html +++ b/browser/base/content/test/general/navigating_window_with_download.html @@ -2,6 +2,6 @@ <html> <head><title>This window will navigate while you're downloading something</title></head> <body> - <iframe src="http://mochi.test:8888/browser/browser/base/content/test/general/unknownContentType_file.pif"></iframe> + <iframe src="https://example.com/browser/browser/base/content/test/general/unknownContentType_file.pif"></iframe> </body> </html> diff --git a/browser/base/content/test/outOfProcess/browser_controller.js b/browser/base/content/test/outOfProcess/browser_controller.js index f9d9ca8c93..f3c0217b90 100644 --- a/browser/base/content/test/outOfProcess/browser_controller.js +++ b/browser/base/content/test/outOfProcess/browser_controller.js @@ -42,8 +42,19 @@ add_task(async function test_controllers_subframes() { gURLBar.focus(); + let canTabMoveFocusToRootElement = !SpecialPowers.getBoolPref( + "dom.disable_tab_focus_to_root_element" + ); for (let stepNum = 0; stepNum < browsingContexts.length; stepNum++) { - await keyAndUpdate(stepNum > 0 ? "VK_TAB" : "VK_F6", {}, 6); + let useTab = stepNum > 0; + // When canTabMoveFocusToRootElement is true, this kepress will move the + // focus to a root element, which will trigger an extra "select" command + // compare to the case when canTabMoveFocusToRootElement is false. + await keyAndUpdate( + useTab ? "VK_TAB" : "VK_F6", + {}, + canTabMoveFocusToRootElement ? 6 : 4 + ); // Since focus may be switching into a separate process here, // need to wait for the focus to have been updated. @@ -59,22 +70,35 @@ add_task(async function test_controllers_subframes() { goUpdateGlobalEditMenuItems(true); } - await SpecialPowers.spawn(browsingContexts[stepNum], [], () => { - // Both the tab key and document navigation with F6 will focus - // the root of the document within the frame. - let document = content.document; - Assert.equal( - document.activeElement, - document.documentElement, - "root focused" + await SpecialPowers.spawn( + browsingContexts[stepNum], + [{ canTabMoveFocusToRootElement, useTab }], + args => { + // Both the tab key and document navigation with F6 will focus + // the root of the document within the frame. + // When dom.disable_tab_focus_to_root_element is true, only F6 will do this. + let document = content.document; + let expectedElement = + args.canTabMoveFocusToRootElement || !args.useTab + ? document.documentElement + : document.getElementById("input"); + Assert.equal(document.activeElement, expectedElement, "root focused"); + } + ); + + if (canTabMoveFocusToRootElement || !useTab) { + // XXX Currently, Copy is always enabled when the root (not an editor element) + // is focused. Possibly that should only be true if a listener is present? + checkCommandState( + "step " + stepNum + " root focused", + false, + true, + false ); - }); - // XXX Currently, Copy is always enabled when the root (not an editor element) - // is focused. Possibly that should only be true if a listener is present? - checkCommandState("step " + stepNum + " root focused", false, true, false); - // Tab to the textbox. - await keyAndUpdate("VK_TAB", {}, 1); + // Tab to the textbox. + await keyAndUpdate("VK_TAB", {}, 1); + } if (AppConstants.platform != "macosx") { goUpdateGlobalEditMenuItems(true); diff --git a/browser/base/content/test/performance/browser_appmenu.js b/browser/base/content/test/performance/browser_appmenu.js index 37e7482c51..984d2b9fd8 100644 --- a/browser/base/content/test/performance/browser_appmenu.js +++ b/browser/base/content/test/performance/browser_appmenu.js @@ -21,15 +21,6 @@ const EXPECTED_APPMENU_OPEN_REFLOWS = [ "openPopup/this._openPopupPromise<@resource:///modules/PanelMultiView.sys.mjs", ], }, - - { - stack: [ - "_calculateMaxHeight@resource:///modules/PanelMultiView.sys.mjs", - "handleEvent@resource:///modules/PanelMultiView.sys.mjs", - ], - - maxCount: 7, // This number should only ever go down - never up. - }, ]; add_task(async function () { diff --git a/browser/base/content/test/performance/browser_startup_content_mainthreadio.js b/browser/base/content/test/performance/browser_startup_content_mainthreadio.js index e60b95bb3c..e267363064 100644 --- a/browser/base/content/test/performance/browser_startup_content_mainthreadio.js +++ b/browser/base/content/test/performance/browser_startup_content_mainthreadio.js @@ -152,6 +152,13 @@ const processes = { condition: WIN, stat: 1, }, + { + // We should remove this in bug 1882427 + path: "*screenshots@mozilla.org.xpi", + condition: true, + ignoreIfUnused: true, + close: 1, + }, ], }; diff --git a/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js b/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js index 4a9276512c..3b027bc1ef 100644 --- a/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js +++ b/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js @@ -5,6 +5,8 @@ const TEST_SECURITY_DELAY = 5000; +SimpleTest.requestCompleteLog(); + /** * Shows a test PopupNotification. */ @@ -400,3 +402,166 @@ add_task(async function test_notificationWindowMove() { // Reset window position window.moveTo(screenX, screenY); }); + +/** + * Tests that the security delay gets extended if a notification is shown during + * a full screen transition. + */ +add_task(async function test_notificationDuringFullScreenTransition() { + // Log full screen transition messages. + let loggingObserver = { + observe(subject, topic) { + info("Observed topic: " + topic); + }, + }; + Services.obs.addObserver(loggingObserver, "fullscreen-transition-start"); + Services.obs.addObserver(loggingObserver, "fullscreen-transition-end"); + // Unregister observers when the test ends: + registerCleanupFunction(() => { + Services.obs.removeObserver(loggingObserver, "fullscreen-transition-start"); + Services.obs.removeObserver(loggingObserver, "fullscreen-transition-end"); + }); + + if (Services.appinfo.OS == "Linux") { + ok( + "Skipping test on Linux because of disabled full screen transition in CI." + ); + return; + } + // Bug 1882527: Intermittent failures on macOS. + if (Services.appinfo.OS == "Darwin") { + ok("Skipping test on macOS because of intermittent failures."); + return; + } + + await BrowserTestUtils.withNewTab("https://example.com", async browser => { + await SpecialPowers.pushPrefEnv({ + set: [ + // Set a short security delay so we can observe it being extended. + ["security.notification_enable_delay", 1], + // Set a longer full screen exit transition so the test works on slow builds. + ["full-screen-api.transition-duration.leave", "1000 1000"], + // Waive the user activation requirement for full screen requests. + // The PoC this test is based on relies on spam clicking which grants + // user activation in the popup that requests full screen. + // This isn't reliable in automation. + ["full-screen-api.allow-trusted-requests-only", false], + // macOS native full screen is not affected by the full screen + // transition overlap. Test with the old full screen implementation. + ["full-screen-api.macos-native-full-screen", false], + ], + }); + + await ensureSecurityDelayReady(); + + ok( + !PopupNotifications.isPanelOpen, + "PopupNotification panel should not be open initially." + ); + + info("Open a notification."); + let popupShownPromise = waitForNotificationPanel(); + showNotification(); + await popupShownPromise; + ok( + PopupNotifications.isPanelOpen, + "PopupNotification should be open after show call." + ); + + let notification = PopupNotifications.getNotification("foo", browser); + is(notification?.id, "foo", "There should be a notification with id foo"); + + info( + "Open a new tab via window.open, enter full screen and remove the tab." + ); + + // There are two transitions, one for full screen entry and one for full screen exit. + let transitionStartCount = 0; + let transitionEndCount = 0; + let promiseFullScreenTransitionStart = TestUtils.topicObserved( + "fullscreen-transition-start", + () => { + transitionStartCount++; + return transitionStartCount == 2; + } + ); + let promiseFullScreenTransitionEnd = TestUtils.topicObserved( + "fullscreen-transition-end", + () => { + transitionEndCount++; + return transitionEndCount == 2; + } + ); + let notificationShownPromise = waitForNotificationPanel(); + + await SpecialPowers.spawn(browser, [], () => { + // Use eval to execute in the privilege context of the website. + content.eval(` + let button = document.createElement("button"); + button.id = "triggerBtn"; + button.innerText = "Open Popup"; + button.addEventListener("click", () => { + let popup = window.open("about:blank"); + popup.document.write( + "<script>setTimeout(() => document.documentElement.requestFullscreen(), 500)</script>" + ); + popup.document.write( + "<script>setTimeout(() => window.close(), 1500)</script>" + ); + }); + // Insert button at the top so the synthesized click works. Otherwise + // the button may be outside of the viewport. + document.body.prepend(button); + `); + }); + + let timeClick = performance.now(); + await BrowserTestUtils.synthesizeMouseAtCenter("#triggerBtn", {}, browser); + + info("Wait for the exit transition to start. It's the second transition."); + await promiseFullScreenTransitionStart; + info("Full screen transition start"); + ok(true, "Full screen transition started"); + ok( + window.isInFullScreenTransition, + "Full screen transition is still running." + ); + + info( + "Wait for notification to re-show on tab switch, after the popup has been closed" + ); + await notificationShownPromise; + ok( + window.isInFullScreenTransition, + "Full screen transition is still running." + ); + info( + "about to trigger notification. time between btn click and notification show: " + + (performance.now() - timeClick) + ); + + info( + "Trigger main action via button click during the extended security delay." + ); + triggerMainCommand(PopupNotifications.panel); + + await new Promise(resolve => setTimeout(resolve, 0)); + + ok( + PopupNotifications.isPanelOpen, + "PopupNotification should still be open." + ); + notification = PopupNotifications.getNotification( + "foo", + gBrowser.selectedBrowser + ); + ok( + notification, + "Notification should still be open because we clicked during the security delay." + ); + + info("Wait for full screen transition end."); + await promiseFullScreenTransitionEnd; + info("Full screen transition end"); + }); +}); diff --git a/browser/base/content/test/popups/browser_popup_blocker.js b/browser/base/content/test/popups/browser_popup_blocker.js index bfda12331e..d2e182b366 100644 --- a/browser/base/content/test/popups/browser_popup_blocker.js +++ b/browser/base/content/test/popups/browser_popup_blocker.js @@ -34,9 +34,18 @@ add_task(async function test_maximum_reported_blocks() { ); // Wait for the popup-blocked notification. - let notification = await TestUtils.waitForCondition(() => - gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked") - ); + let notification = await TestUtils.waitForCondition(() => { + let tempNotif = gBrowser + .getNotificationBox() + .getNotificationWithValue("popup-blocked"); + + // The textContent in the notification object is contains + // blank spaces as placeholder if there is no value in it + if (tempNotif?.messageText.textContent.trim().length) { + return tempNotif; + } + return false; + }); // Slightly hacky way to ensure we show the correct message in this case. ok( diff --git a/browser/base/content/test/privateBrowsing/browser_private_browsing_simplified_ui.js b/browser/base/content/test/privateBrowsing/browser_private_browsing_simplified_ui.js index 162bf7ab5a..c52ed4187a 100644 --- a/browser/base/content/test/privateBrowsing/browser_private_browsing_simplified_ui.js +++ b/browser/base/content/test/privateBrowsing/browser_private_browsing_simplified_ui.js @@ -30,19 +30,4 @@ add_task(async function check_for_simplified_pbm_ui() { ); await BrowserTestUtils.closeWindow(pbmWindow); - await SpecialPowers.pushPrefEnv({ - set: [["browser.toolbars.bookmarks.showInPrivateBrowsing", true]], - }); - pbmWindow = await BrowserTestUtils.openNewBrowserWindow({ - private: true, - }); - bookmarksBar = pbmWindow.document.getElementById("PersonalToolbar"); - console.info(bookmarksBar.getAttribute("collapsed")); - Assert.equal( - bookmarksBar.getAttribute("collapsed").toString(), - "false", - "Bookmarks bar is visible in PBM window when showInPrivateBrowsing pref is true" - ); - - await BrowserTestUtils.closeWindow(pbmWindow); }); diff --git a/browser/base/content/test/protectionsUI/browser.toml b/browser/base/content/test/protectionsUI/browser.toml index 10611cbe8c..f3c64eb67f 100644 --- a/browser/base/content/test/protectionsUI/browser.toml +++ b/browser/base/content/test/protectionsUI/browser.toml @@ -59,13 +59,6 @@ https_first_disabled = true ["browser_protectionsUI_pbmode_exceptions.js"] https_first_disabled = true -["browser_protectionsUI_report_breakage.js"] -https_first_disabled = true -skip-if = [ - "debug", # Bug 1546797 - "asan", -] - ["browser_protectionsUI_shield_visibility.js"] support-files = [ "sandboxed.html", diff --git a/browser/base/content/test/protectionsUI/browser_protectionsUI.js b/browser/base/content/test/protectionsUI/browser_protectionsUI.js index 98698e087e..5dc6acebf7 100644 --- a/browser/base/content/test/protectionsUI/browser_protectionsUI.js +++ b/browser/base/content/test/protectionsUI/browser_protectionsUI.js @@ -73,85 +73,16 @@ add_task(async function testToggleSwitch() { console.log(buttonEvents); is(buttonEvents.length, 1, "recorded telemetry for opening the popup"); - // Check the visibility of the "Site not working?" link. - ok( - BrowserTestUtils.isVisible( - gProtectionsHandler._protectionsPopupTPSwitchBreakageLink - ), - "The 'Site not working?' link should be visible." - ); - - // The 'Site Fixed?' link should be hidden. - ok( - BrowserTestUtils.isHidden( - gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink - ), - "The 'Site Fixed?' link should be hidden." - ); - - // Navigate through the 'Site Not Working?' flow and back to the main view, - // checking for telemetry on the way. - let siteNotWorkingView = document.getElementById( - "protections-popup-siteNotWorkingView" - ); - let viewShown = BrowserTestUtils.waitForEvent( - siteNotWorkingView, - "ViewShown" - ); - gProtectionsHandler._protectionsPopupTPSwitchBreakageLink.click(); - await viewShown; - - checkClickTelemetry("sitenotworking_link"); - - let sendReportButton = document.getElementById( - "protections-popup-siteNotWorkingView-sendReport" - ); - let sendReportView = document.getElementById( - "protections-popup-sendReportView" - ); - viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); - sendReportButton.click(); - await viewShown; - - checkClickTelemetry("send_report_link"); - - viewShown = BrowserTestUtils.waitForEvent(siteNotWorkingView, "ViewShown"); - sendReportView.querySelector(".subviewbutton-back").click(); - await viewShown; - - let mainView = document.getElementById("protections-popup-mainView"); - - viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); - siteNotWorkingView.querySelector(".subviewbutton-back").click(); - await viewShown; + let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); - ok( - gProtectionsHandler._protectionsPopupTPSwitch.hasAttribute("pressed"), - "TP Switch should be on" - ); let popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); - let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); - await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); - - // The 'Site not working?' link should be hidden after clicking the TP switch. - ok( - BrowserTestUtils.isHidden( - gProtectionsHandler._protectionsPopupTPSwitchBreakageLink - ), - "The 'Site not working?' link should be hidden after TP switch turns to off." - ); - // Same for the 'Site Fixed?' link - ok( - BrowserTestUtils.isHidden( - gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink - ), - "The 'Site Fixed?' link should be hidden." - ); + await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); await popuphiddenPromise; + checkClickTelemetry("etp_toggle_off"); // We need to wait toast's popup shown and popup hidden events. It won't fire @@ -164,41 +95,13 @@ add_task(async function testToggleSwitch() { // Wait until the ETP state confirmation toast is shown and hides itself. await toastShown; + // Re-open the protections panel and confirm that the toggle is off, then toggle it back on. await openProtectionsPanel(); ok( !gProtectionsHandler._protectionsPopupTPSwitch.hasAttribute("pressed"), "TP Switch should be off" ); - // The 'Site not working?' link should be hidden if the TP is off. - ok( - BrowserTestUtils.isHidden( - gProtectionsHandler._protectionsPopupTPSwitchBreakageLink - ), - "The 'Site not working?' link should be hidden if TP is off." - ); - - // The 'Site Fixed?' link should be shown if TP is off. - ok( - BrowserTestUtils.isVisible( - gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink - ), - "The 'Site Fixed?' link should be visible." - ); - - // Check telemetry for 'Site Fixed?' link. - viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); - gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink.click(); - await viewShown; - - checkClickTelemetry("sitenotworking_link", "sitefixed"); - - viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); - sendReportView.querySelector(".subviewbutton-back").click(); - await viewShown; - - // Click the TP switch again and check the visibility of the 'Site not - // Working?'. It should be hidden after toggling the TP switch. browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); popuphiddenPromise = BrowserTestUtils.waitForEvent( @@ -208,20 +111,6 @@ add_task(async function testToggleSwitch() { await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); - ok( - BrowserTestUtils.isHidden( - gProtectionsHandler._protectionsPopupTPSwitchBreakageLink - ), - `The 'Site not working?' link should be still hidden after toggling TP - switch to on from off.` - ); - ok( - BrowserTestUtils.isHidden( - gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink - ), - "The 'Site Fixed?' link should be hidden." - ); - // Wait for the protections panel to be hidden as the result of the ETP toggle // on action. await popuphiddenPromise; diff --git a/browser/base/content/test/protectionsUI/browser_protectionsUI_report_breakage.js b/browser/base/content/test/protectionsUI/browser_protectionsUI_report_breakage.js deleted file mode 100644 index 2ae0d5c9d9..0000000000 --- a/browser/base/content/test/protectionsUI/browser_protectionsUI_report_breakage.js +++ /dev/null @@ -1,415 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const TRACKING_PAGE = - // eslint-disable-next-line @microsoft/sdl/no-insecure-url - "http://tracking.example.org/browser/browser/base/content/test/protectionsUI/trackingPage.html"; -const BENIGN_PAGE = - // eslint-disable-next-line @microsoft/sdl/no-insecure-url - "http://tracking.example.org/browser/browser/base/content/test/protectionsUI/benignPage.html"; -const COOKIE_PAGE = - // eslint-disable-next-line @microsoft/sdl/no-insecure-url - "http://not-tracking.example.com/browser/browser/base/content/test/protectionsUI/cookiePage.html"; - -const CM_PREF = "privacy.trackingprotection.cryptomining.enabled"; -const FP_PREF = "privacy.trackingprotection.fingerprinting.enabled"; -const TP_PREF = "privacy.trackingprotection.enabled"; -const CB_PREF = "network.cookie.cookieBehavior"; -const GPC_PREF = "privacy.globalprivacycontrol.enabled"; - -const PREF_REPORT_BREAKAGE_URL = "browser.contentblocking.reportBreakage.url"; - -let { HttpServer } = ChromeUtils.importESModule( - "resource://testing-common/httpd.sys.mjs" -); -let { CommonUtils } = ChromeUtils.importESModule( - "resource://services-common/utils.sys.mjs" -); -let { Preferences } = ChromeUtils.importESModule( - "resource://gre/modules/Preferences.sys.mjs" -); - -add_setup(async function () { - await UrlClassifierTestUtils.addTestTrackers(); - - // Disable Report Broken Site, as it hides "Site not working?" when enabled. - await SpecialPowers.pushPrefEnv({ - set: [["ui.new-webcompat-reporter.enabled", false]], - }); - - registerCleanupFunction(() => { - // Clear prefs that are touched in this test again for sanity. - Services.prefs.clearUserPref(TP_PREF); - Services.prefs.clearUserPref(CB_PREF); - Services.prefs.clearUserPref(FP_PREF); - Services.prefs.clearUserPref(CM_PREF); - Services.prefs.clearUserPref(GPC_PREF); - Services.prefs.clearUserPref(PREF_REPORT_BREAKAGE_URL); - - UrlClassifierTestUtils.cleanupTestTrackers(); - }); - - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "urlclassifier.features.fingerprinting.blacklistHosts", - "fingerprinting.example.com", - ], - [ - "urlclassifier.features.fingerprinting.annotate.blacklistHosts", - "fingerprinting.example.com", - ], - ["privacy.trackingprotection.cryptomining.enabled", true], - [ - "urlclassifier.features.cryptomining.blacklistHosts", - "cryptomining.example.com", - ], - [ - "urlclassifier.features.cryptomining.annotate.blacklistHosts", - "cryptomining.example.com", - ], - ["privacy.globalprivacycontrol.enabled", true], - ], - }); -}); - -add_task(async function testReportBreakageCancel() { - Services.prefs.setBoolPref(TP_PREF, true); - - await BrowserTestUtils.withNewTab(TRACKING_PAGE, async function () { - await openProtectionsPanel(); - await TestUtils.waitForCondition(() => - gProtectionsHandler._protectionsPopup.hasAttribute("blocking") - ); - - let siteNotWorkingButton = document.getElementById( - "protections-popup-tp-switch-breakage-link" - ); - ok( - BrowserTestUtils.isVisible(siteNotWorkingButton), - "site not working button is visible" - ); - let siteNotWorkingView = document.getElementById( - "protections-popup-siteNotWorkingView" - ); - let viewShown = BrowserTestUtils.waitForEvent( - siteNotWorkingView, - "ViewShown" - ); - siteNotWorkingButton.click(); - await viewShown; - - let sendReportButton = document.getElementById( - "protections-popup-siteNotWorkingView-sendReport" - ); - let sendReportView = document.getElementById( - "protections-popup-sendReportView" - ); - viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); - sendReportButton.click(); - await viewShown; - - ok(true, "Report breakage view was shown"); - - viewShown = BrowserTestUtils.waitForEvent(siteNotWorkingView, "ViewShown"); - let cancelButton = document.getElementById( - "protections-popup-sendReportView-cancel" - ); - cancelButton.click(); - await viewShown; - - ok(true, "Main view was shown"); - }); - - Services.prefs.clearUserPref(TP_PREF); -}); - -add_task(async function testReportBreakageSiteException() { - Services.prefs.setBoolPref(TP_PREF, true); - - let url = TRACKING_PAGE + "?a=b&1=abc&unicode=🦊"; - - await BrowserTestUtils.withNewTab(url, async browser => { - let loaded = BrowserTestUtils.browserLoaded(browser, false); - gProtectionsHandler.disableForCurrentPage(); - await loaded; - - await openProtectionsPanel(); - - let siteFixedButton = document.getElementById( - "protections-popup-tp-switch-breakage-fixed-link" - ); - ok( - BrowserTestUtils.isVisible(siteFixedButton), - "site fixed button is visible" - ); - let sendReportView = document.getElementById( - "protections-popup-sendReportView" - ); - let viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); - siteFixedButton.click(); - await viewShown; - - ok(true, "Report breakage view was shown"); - - await testReportBreakageSubmit( - TRACKING_PAGE, - "trackingprotection", - false, - true - ); - - // Pass false for shouldReload - there's no need since the tab is going away. - gProtectionsHandler.enableForCurrentPage(false); - }); - - Services.prefs.clearUserPref(TP_PREF); -}); - -add_task(async function testNoTracking() { - await BrowserTestUtils.withNewTab(BENIGN_PAGE, async function () { - await openProtectionsPanel(); - - let siteNotWorkingButton = document.getElementById( - "protections-popup-tp-switch-breakage-link" - ); - ok( - BrowserTestUtils.isHidden(siteNotWorkingButton), - "site not working button is not visible" - ); - }); -}); - -add_task(async function testReportBreakageError() { - Services.prefs.setBoolPref(TP_PREF, true); - // Make sure that we correctly strip the query. - let url = TRACKING_PAGE + "?a=b&1=abc&unicode=🦊"; - await BrowserTestUtils.withNewTab(url, async function () { - await openAndTestReportBreakage(TRACKING_PAGE, "trackingprotection", true); - }); - - Services.prefs.clearUserPref(TP_PREF); -}); - -add_task(async function testTP() { - Services.prefs.setBoolPref(TP_PREF, true); - // Make sure that we correctly strip the query. - let url = TRACKING_PAGE + "?a=b&1=abc&unicode=🦊"; - await BrowserTestUtils.withNewTab(url, async function () { - await openAndTestReportBreakage(TRACKING_PAGE, "trackingprotection"); - }); - - Services.prefs.clearUserPref(TP_PREF); -}); - -add_task(async function testCR() { - Services.prefs.setIntPref( - CB_PREF, - Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER - ); - // Make sure that we correctly strip the query. - let url = COOKIE_PAGE + "?a=b&1=abc&unicode=🦊"; - await BrowserTestUtils.withNewTab(url, async function () { - await openAndTestReportBreakage(COOKIE_PAGE, "cookierestrictions"); - }); - - Services.prefs.clearUserPref(CB_PREF); -}); - -add_task(async function testFP() { - Services.prefs.setIntPref(CB_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT); - Services.prefs.setBoolPref(FP_PREF, true); - // Make sure that we correctly strip the query. - let url = TRACKING_PAGE + "?a=b&1=abc&unicode=🦊"; - await BrowserTestUtils.withNewTab(url, async function (browser) { - let promise = waitForContentBlockingEvent(); - await SpecialPowers.spawn(browser, [], function () { - content.postMessage("fingerprinting", "*"); - }); - await promise; - - await openAndTestReportBreakage(TRACKING_PAGE, "fingerprinting", true); - }); - - Services.prefs.clearUserPref(FP_PREF); - Services.prefs.clearUserPref(CB_PREF); -}); - -add_task(async function testCM() { - Services.prefs.setIntPref(CB_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT); - Services.prefs.setBoolPref(CM_PREF, true); - // Make sure that we correctly strip the query. - let url = TRACKING_PAGE + "?a=b&1=abc&unicode=🦊"; - await BrowserTestUtils.withNewTab(url, async function (browser) { - let promise = waitForContentBlockingEvent(); - await SpecialPowers.spawn(browser, [], function () { - content.postMessage("cryptomining", "*"); - }); - await promise; - - await openAndTestReportBreakage(TRACKING_PAGE, "cryptomining", true); - }); - - Services.prefs.clearUserPref(CM_PREF); - Services.prefs.clearUserPref(CB_PREF); -}); - -async function openAndTestReportBreakage(url, tags, error = false) { - await openProtectionsPanel(); - - let siteNotWorkingButton = document.getElementById( - "protections-popup-tp-switch-breakage-link" - ); - ok( - BrowserTestUtils.isVisible(siteNotWorkingButton), - "site not working button is visible" - ); - let siteNotWorkingView = document.getElementById( - "protections-popup-siteNotWorkingView" - ); - let viewShown = BrowserTestUtils.waitForEvent( - siteNotWorkingView, - "ViewShown" - ); - siteNotWorkingButton.click(); - await viewShown; - - let sendReportButton = document.getElementById( - "protections-popup-siteNotWorkingView-sendReport" - ); - let sendReportView = document.getElementById( - "protections-popup-sendReportView" - ); - viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); - sendReportButton.click(); - await viewShown; - - ok(true, "Report breakage view was shown"); - - await testReportBreakageSubmit(url, tags, error, false); -} - -// This function assumes that the breakage report view is ready. -async function testReportBreakageSubmit(url, tags, error, hasException) { - // Setup a mock server for receiving breakage reports. - let server = new HttpServer(); - server.start(-1); - let i = server.identity; - let path = - i.primaryScheme + "://" + i.primaryHost + ":" + i.primaryPort + "/"; - - Services.prefs.setStringPref(PREF_REPORT_BREAKAGE_URL, path); - - let comments = document.getElementById( - "protections-popup-sendReportView-collection-comments" - ); - is(comments.value, "", "Comments textarea should initially be empty"); - - let submitButton = document.getElementById( - "protections-popup-sendReportView-submit" - ); - let reportURL = document.getElementById( - "protections-popup-sendReportView-collection-url" - ).value; - - is(reportURL, url, "Shows the correct URL in the report UI."); - - // Make sure that sending the report closes the identity popup. - let popuphidden = BrowserTestUtils.waitForEvent( - gProtectionsHandler._protectionsPopup, - "popuphidden" - ); - - // Check that we're receiving a good report. - await new Promise(resolve => { - server.registerPathHandler("/", async (request, response) => { - is(request.method, "POST", "request was a post"); - - // Extract and "parse" the form data in the request body. - let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream); - let boundary = request - .getHeader("Content-Type") - .match(/boundary=-+([^-]*)/i)[1]; - let regex = new RegExp("-+" + boundary + "-*\\s+"); - let sections = body.split(regex); - - let prefs = [ - "privacy.trackingprotection.enabled", - "privacy.trackingprotection.pbmode.enabled", - "urlclassifier.trackingTable", - "network.http.referer.defaultPolicy", - "network.http.referer.defaultPolicy.pbmode", - "network.cookie.cookieBehavior", - "privacy.annotate_channels.strict_list.enabled", - "privacy.restrict3rdpartystorage.expiration", - "privacy.trackingprotection.fingerprinting.enabled", - "privacy.trackingprotection.cryptomining.enabled", - "privacy.globalprivacycontrol.enabled", - ]; - let prefsBody = ""; - - for (let pref of prefs) { - prefsBody += `${pref}: ${Preferences.get(pref)}\r\n`; - } - - Assert.deepEqual( - sections, - [ - "", - `Content-Disposition: form-data; name=\"title\"\r\n\r\n${ - Services.io.newURI(reportURL).host - }\r\n`, - 'Content-Disposition: form-data; name="body"\r\n\r\n' + - `Full URL: ${reportURL + "?"}\r\n` + - `userAgent: ${navigator.userAgent}\r\n\r\n` + - "**Preferences**\r\n" + - `${prefsBody}\r\n` + - `hasException: ${hasException}\r\n\r\n` + - "**Comments**\r\n" + - "This is a comment\r\n", - 'Content-Disposition: form-data; name="labels"\r\n\r\n' + - `${hasException ? "" : tags}\r\n`, - "", - ], - "Should send the correct form data" - ); - - if (error) { - response.setStatusLine(request.httpVersion, 500, "Request failed"); - } else { - response.setStatusLine(request.httpVersion, 201, "Entry created"); - } - - resolve(); - }); - - comments.value = "This is a comment"; - submitButton.click(); - }); - - let errorMessage = document.getElementById( - "protections-popup-sendReportView-report-error" - ); - if (error) { - await TestUtils.waitForCondition(() => - BrowserTestUtils.isVisible(errorMessage) - ); - is( - comments.value, - "This is a comment", - "Comment not cleared in case of an error" - ); - gProtectionsHandler._protectionsPopup.hidePopup(); - } else { - ok(BrowserTestUtils.isHidden(errorMessage), "Error message not shown"); - } - - await popuphidden; - - // Stop the server. - await new Promise(r => server.stop(r)); - - Services.prefs.clearUserPref(PREF_REPORT_BREAKAGE_URL); -} diff --git a/browser/base/content/test/sanitize/browser.toml b/browser/base/content/test/sanitize/browser.toml index 814dc54c3d..3c53723833 100644 --- a/browser/base/content/test/sanitize/browser.toml +++ b/browser/base/content/test/sanitize/browser.toml @@ -36,4 +36,6 @@ support-files = [ ["browser_sanitizeDialog_v2.js"] +["browser_sanitizeDialog_v2_dataSizes.js"] + ["browser_sanitizeOnShutdown_migration.js"] diff --git a/browser/base/content/test/sanitize/browser_sanitize-timespans.js b/browser/base/content/test/sanitize/browser_sanitize-timespans.js index 30ccb90666..f9be12775b 100644 --- a/browser/base/content/test/sanitize/browser_sanitize-timespans.js +++ b/browser/base/content/test/sanitize/browser_sanitize-timespans.js @@ -8,9 +8,6 @@ const { PlacesTestUtils } = ChromeUtils.importESModule( var now_mSec = Date.now(); var now_uSec = now_mSec * 1000; -const kMsecPerMin = 60 * 1000; -const kUsecPerMin = 60 * 1000000; - function promiseFormHistoryRemoved() { return new Promise(resolve => { Services.obs.addObserver(function onfh() { diff --git a/browser/base/content/test/sanitize/browser_sanitizeDialog.js b/browser/base/content/test/sanitize/browser_sanitizeDialog.js index 2df7d83c6e..a1e8a5dc85 100644 --- a/browser/base/content/test/sanitize/browser_sanitizeDialog.js +++ b/browser/base/content/test/sanitize/browser_sanitizeDialog.js @@ -22,9 +22,6 @@ ChromeUtils.defineESModuleGetters(this, { Timer: "resource://gre/modules/Timer.sys.mjs", }); -const kMsecPerMin = 60 * 1000; -const kUsecPerMin = 60 * 1000000; - /** * Ensures that the specified URIs are either cleared or not. * @@ -694,10 +691,6 @@ DialogHelper.prototype = { }, }; -function promiseSanitizationComplete() { - return TestUtils.topicObserved("sanitizer-sanitization-complete"); -} - /** * Adds a download to history. * @@ -752,21 +745,6 @@ async function formNameExists(name) { } /** - * Removes all history visits, downloads, and form entries. - */ -async function blankSlate() { - let publicList = await Downloads.getList(Downloads.PUBLIC); - let downloads = await publicList.getAll(); - for (let download of downloads) { - await publicList.remove(download); - await download.finalize(true); - } - - await FormHistory.update({ op: "remove" }); - await PlacesUtils.history.clear(); -} - -/** * Ensures that the given pref is the expected value. * * @param aPrefName diff --git a/browser/base/content/test/sanitize/browser_sanitizeDialog_v2.js b/browser/base/content/test/sanitize/browser_sanitizeDialog_v2.js index 29f760f57f..8ae0263c82 100644 --- a/browser/base/content/test/sanitize/browser_sanitizeDialog_v2.js +++ b/browser/base/content/test/sanitize/browser_sanitizeDialog_v2.js @@ -20,33 +20,9 @@ ChromeUtils.defineESModuleGetters(this, { PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs", Timer: "resource://gre/modules/Timer.sys.mjs", PermissionTestUtils: "resource://testing-common/PermissionTestUtils.sys.mjs", - FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs", Downloads: "resource://gre/modules/Downloads.sys.mjs", }); -const kMsecPerMin = 60 * 1000; -const kUsecPerMin = 60 * 1000000; -let today = Date.now() - new Date().setHours(0, 0, 0, 0); -let nowMSec = Date.now(); -let nowUSec = nowMSec * 1000; -let fileURL; - -const TEST_TARGET_FILE_NAME = "test-download.txt"; -const TEST_QUOTA_USAGE_HOST = "example.com"; -const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST; -const TEST_QUOTA_USAGE_URL = - getRootDirectory(gTestPath).replace( - "chrome://mochitests/content", - TEST_QUOTA_USAGE_ORIGIN - ) + "site_data_test.html"; - -const siteOrigins = [ - "https://www.example.com", - "https://example.org", - "http://localhost:8000", - "http://localhost:3000", -]; - /** * Ensures that the specified URIs are either cleared or not. * @@ -167,90 +143,6 @@ async function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) { aExpectedPathList.push(name); } -/** - * Adds multiple downloads to the PUBLIC download list - */ -async function addToDownloadList() { - const url = createFileURL(); - const downloadsList = await Downloads.getList(Downloads.PUBLIC); - let timeOptions = [1, 2, 4, 24, 128, 128]; - let buffer = 100000; - - for (let i = 0; i < timeOptions.length; i++) { - let timeDownloaded = 60 * kMsecPerMin * timeOptions[i]; - if (timeOptions[i] === 24) { - timeDownloaded = today; - } - - let download = await Downloads.createDownload({ - source: { url: url.spec, isPrivate: false }, - target: { path: FileTestUtils.getTempFile(TEST_TARGET_FILE_NAME).path }, - startTime: { - getTime: _ => { - return nowMSec - timeDownloaded + buffer; - }, - }, - }); - - Assert.ok(!!download); - downloadsList.add(download); - } - let items = await downloadsList.getAll(); - Assert.equal(items.length, 6, "Items were added to the list"); -} - -async function addToSiteUsage() { - // Fill indexedDB with test data. - // Don't wait for the page to load, to register the content event handler as quickly as possible. - // If this test goes intermittent, we might have to tell the page to wait longer before - // firing the event. - BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL, false); - await BrowserTestUtils.waitForContentEvent( - gBrowser.selectedBrowser, - "test-indexedDB-done", - false, - null, - true - ); - BrowserTestUtils.removeTab(gBrowser.selectedTab); - - let siteLastAccessed = [1, 2, 4, 24]; - - let staticUsage = 4096 * 6; - // Add a time buffer so the site access falls within the time range - const buffer = 10000; - - // Change lastAccessed of sites - for (let index = 0; index < siteLastAccessed.length; index++) { - let lastAccessedTime = 60 * kMsecPerMin * siteLastAccessed[index]; - if (siteLastAccessed[index] === 24) { - lastAccessedTime = today; - } - - let site = SiteDataManager._testInsertSite(siteOrigins[index], { - quotaUsage: staticUsage, - lastAccessed: (nowMSec - lastAccessedTime + buffer) * 1000, - }); - Assert.ok(site, "Site added successfully"); - } -} - -/** - * Helper function to create file URL to open - * - * @returns {Object} a file URL - */ -function createFileURL() { - if (!fileURL) { - let file = Services.dirsvc.get("TmpD", Ci.nsIFile); - file.append("foo.txt"); - file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); - - fileURL = Services.io.newFileURI(file); - } - return fileURL; -} - add_setup(async function () { requestLongerTimeout(3); await blankSlate(); @@ -268,248 +160,6 @@ add_setup(async function () { }); /** - * Removes all history visits, downloads, and form entries. - */ -async function blankSlate() { - let publicList = await Downloads.getList(Downloads.PUBLIC); - let downloads = await publicList.getAll(); - for (let download of downloads) { - await publicList.remove(download); - await download.finalize(true); - } - - await FormHistory.update({ op: "remove" }); - await PlacesUtils.history.clear(); -} - -/** - * This wraps the dialog and provides some convenience methods for interacting - * with it. - * - * @param browserWin (optional) - * The browser window that the dialog is expected to open in. If not - * supplied, the initial browser window of the test run is used. - * @param mode (optional) - * One of - * clear on shutdown settings context ("clearOnShutdown"), - * clear site data settings context ("clearSiteData"), - * clear history context ("clearHistory"), - * browser context ("browser") - * "browser" by default - */ -function DialogHelper(openContext = "browser") { - this._browserWin = window; - this.win = null; - this._mode = openContext; - this.promiseClosed = new Promise(resolve => { - this._resolveClosed = resolve; - }); -} - -DialogHelper.prototype = { - /** - * "Presses" the dialog's OK button. - */ - acceptDialog() { - let dialogEl = this.win.document.querySelector("dialog"); - is( - dialogEl.getButton("accept").disabled, - false, - "Dialog's OK button should not be disabled" - ); - dialogEl.acceptDialog(); - }, - - /** - * "Presses" the dialog's Cancel button. - */ - cancelDialog() { - this.win.document.querySelector("dialog").cancelDialog(); - }, - - /** - * (Un)checks a history scope checkbox (browser & download history, - * form history, etc.). - * - * @param aPrefName - * The final portion of the checkbox's privacy.cpd.* preference name - * @param aCheckState - * True if the checkbox should be checked, false otherwise - */ - checkPrefCheckbox(aPrefName, aCheckState) { - var cb = this.win.document.querySelectorAll( - "checkbox[id='" + aPrefName + "']" - ); - is(cb.length, 1, "found checkbox for " + aPrefName + " id"); - if (cb[0].checked != aCheckState) { - cb[0].click(); - } - }, - - /** - * @param {String} aCheckboxId - * The checkbox id name - * @param {Boolean} aCheckState - * True if the checkbox should be checked, false otherwise - */ - validateCheckbox(aCheckboxId, aCheckState) { - let cb = this.win.document.querySelectorAll( - "checkbox[id='" + aCheckboxId + "']" - ); - is(cb.length, 1, `found checkbox for id=${aCheckboxId}`); - is( - cb[0].checked, - aCheckState, - `checkbox for ${aCheckboxId} is ${aCheckState}` - ); - }, - - /** - * Makes sure all the checkboxes are checked. - */ - _checkAllCheckboxesCustom(check) { - var cb = this.win.document.querySelectorAll(".clearingItemCheckbox"); - ok(cb.length, "found checkboxes for ids"); - for (var i = 0; i < cb.length; ++i) { - if (cb[i].checked != check) { - cb[i].click(); - } - } - }, - - checkAllCheckboxes() { - this._checkAllCheckboxesCustom(true); - }, - - uncheckAllCheckboxes() { - this._checkAllCheckboxesCustom(false); - }, - - /** - * @return The dialog's duration dropdown - */ - getDurationDropdown() { - return this.win.document.getElementById("sanitizeDurationChoice"); - }, - - /** - * @return The clear-everything warning box - */ - getWarningPanel() { - return this.win.document.getElementById("sanitizeEverythingWarningBox"); - }, - - /** - * @return True if the "Everything" warning panel is visible (as opposed to - * the tree) - */ - isWarningPanelVisible() { - return !this.getWarningPanel().hidden; - }, - - /** - * Opens the clear recent history dialog. Before calling this, set - * this.onload to a function to execute onload. It should close the dialog - * when done so that the tests may continue. Set this.onunload to a function - * to execute onunload. this.onunload is optional. If it returns true, the - * caller is expected to call promiseAsyncUpdates at some point; if false is - * returned, promiseAsyncUpdates is called automatically. - */ - async open() { - let dialogPromise = BrowserTestUtils.promiseAlertDialogOpen( - null, - "chrome://browser/content/sanitize_v2.xhtml", - { - isSubDialog: true, - } - ); - - // We want to simulate opening the dialog inside preferences for clear history - // and clear site data - if (this._mode != "browser") { - await openPreferencesViaOpenPreferencesAPI("privacy", { - leaveOpen: true, - }); - let tabWindow = gBrowser.selectedBrowser.contentWindow; - let clearDialogOpenButtonId = this._mode + "Button"; - // the id for clear on shutdown is of a different format - if (this._mode == "clearOnShutdown") { - // set always clear to true to enable the clear on shutdown dialog - let enableSettingsCheckbox = - tabWindow.document.getElementById("alwaysClear"); - if (!enableSettingsCheckbox.checked) { - enableSettingsCheckbox.click(); - } - clearDialogOpenButtonId = "clearDataSettings"; - } - // open dialog - tabWindow.document.getElementById(clearDialogOpenButtonId).click(); - } - // We open the dialog in the chrome context in other cases - else { - executeSoon(() => { - Sanitizer.showUI(this._browserWin, this._mode); - }); - } - - this.win = await dialogPromise; - this.win.addEventListener( - "load", - () => { - // Run onload on next tick so that gSanitizePromptDialog.init can run first. - executeSoon(async () => { - await this.win.gSanitizePromptDialog.dataSizesFinishedUpdatingPromise; - this.onload(); - }); - }, - { once: true } - ); - this.win.addEventListener( - "unload", - () => { - // Some exceptions that reach here don't reach the test harness, but - // ok()/is() do... - (async () => { - if (this.onunload) { - await this.onunload(); - } - if (this._mode != "browser") { - BrowserTestUtils.removeTab(gBrowser.selectedTab); - } - await PlacesTestUtils.promiseAsyncUpdates(); - this._resolveClosed(); - this.win = null; - })(); - }, - { once: true } - ); - }, - - /** - * Selects a duration in the duration dropdown. - * - * @param aDurVal - * One of the Sanitizer.TIMESPAN_* values - */ - selectDuration(aDurVal) { - this.getDurationDropdown().value = aDurVal; - if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) { - is( - this.isWarningPanelVisible(), - true, - "Warning panel should be visible for TIMESPAN_EVERYTHING" - ); - } else { - is( - this.isWarningPanelVisible(), - false, - "Warning panel should not be visible for non-TIMESPAN_EVERYTHING" - ); - } - }, -}; - -/** * Ensures that the given pref is the expected value. * * @param aPrefName @@ -533,58 +183,6 @@ function visitTimeForMinutesAgo(aMinutesAgo) { return nowUSec - aMinutesAgo * kUsecPerMin; } -function promiseSanitizationComplete() { - return TestUtils.topicObserved("sanitizer-sanitization-complete"); -} - -/** - * Helper function to validate the data sizes shown for each time selection - * - * @param {DialogHelper} dh - dialog object to access window and timespan - */ -async function validateDataSizes(dialogHelper) { - let timespans = [ - "TIMESPAN_HOUR", - "TIMESPAN_2HOURS", - "TIMESPAN_4HOURS", - "TIMESPAN_TODAY", - "TIMESPAN_EVERYTHING", - ]; - - // get current data sizes from siteDataManager - let cacheUsage = await SiteDataManager.getCacheSize(); - let quotaUsage = await SiteDataManager.getQuotaUsageForTimeRanges(timespans); - - for (let i = 0; i < timespans.length; i++) { - // select timespan to check - dialogHelper.selectDuration(Sanitizer[timespans[i]]); - - // get the elements - let clearCookiesAndSiteDataCheckbox = - dialogHelper.win.document.getElementById("cookiesAndStorage"); - let clearCacheCheckbox = dialogHelper.win.document.getElementById("cache"); - - let [convertedQuotaUsage] = DownloadUtils.convertByteUnits( - quotaUsage[timespans[i]] - ); - let [, convertedCacheUnit] = DownloadUtils.convertByteUnits(cacheUsage); - - // Ensure l10n is finished before inspecting the category labels. - await dialogHelper.win.document.l10n.translateElements([ - clearCookiesAndSiteDataCheckbox, - clearCacheCheckbox, - ]); - ok( - clearCacheCheckbox.label.includes(convertedCacheUnit), - "Should show the cache usage" - ); - ok( - clearCookiesAndSiteDataCheckbox.label.includes(convertedQuotaUsage), - `Should show the quota usage as ${convertedQuotaUsage}` - ); - } -} - /** * * Opens dialog in the provided context and selects the checkboxes @@ -602,7 +200,7 @@ async function performActionsOnDialog({ cache = false, siteSettings = false, }) { - let dh = new DialogHelper(context); + let dh = new ClearHistoryDialogHelper({ mode: context }); dh.onload = function () { this.selectDuration(timespan); this.checkPrefCheckbox( @@ -622,7 +220,7 @@ async function performActionsOnDialog({ * Initializes the dialog to its default state. */ add_task(async function default_state() { - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { // Select "Last Hour" this.selectDuration(Sanitizer.TIMESPAN_HOUR); @@ -647,7 +245,7 @@ add_task(async function test_cancel() { } await PlacesTestUtils.addVisits(places); - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { this.selectDuration(Sanitizer.TIMESPAN_HOUR); this.checkPrefCheckbox("historyFormDataAndDownloads", false); @@ -662,6 +260,72 @@ add_task(async function test_cancel() { await dh.promiseClosed; }); +// test remembering user options for various entry points +add_task(async function test_pref_remembering() { + let dh = new ClearHistoryDialogHelper({ mode: "clearSiteData" }); + dh.onload = function () { + this.checkPrefCheckbox("cookiesAndStorage", false); + this.checkPrefCheckbox("siteSettings", true); + + this.acceptDialog(); + }; + dh.open(); + await dh.promiseClosed; + + // validate if prefs are remembered + dh = new ClearHistoryDialogHelper({ mode: "clearSiteData" }); + dh.onload = function () { + this.validateCheckbox("cookiesAndStorage", false); + this.validateCheckbox("siteSettings", true); + + this.checkPrefCheckbox("cookiesAndStorage", true); + this.checkPrefCheckbox("siteSettings", false); + + // we will test cancelling the dialog, to make sure it doesn't remember + // the prefs when cancelled + this.cancelDialog(); + }; + dh.open(); + await dh.promiseClosed; + + // validate if prefs did not change since we cancelled the dialog + dh = new ClearHistoryDialogHelper({ mode: "clearSiteData" }); + dh.onload = function () { + this.validateCheckbox("cookiesAndStorage", false); + this.validateCheckbox("siteSettings", true); + + this.cancelDialog(); + }; + dh.open(); + await dh.promiseClosed; + + // test rememebering prefs from the clear history context + // since clear history and clear site data have seperate remembering + // of prefs + dh = new ClearHistoryDialogHelper({ mode: "clearHistory" }); + dh.onload = function () { + this.checkPrefCheckbox("cookiesAndStorage", true); + this.checkPrefCheckbox("siteSettings", false); + this.checkPrefCheckbox("cache", false); + + this.acceptDialog(); + }; + dh.open(); + await dh.promiseClosed; + + // validate if prefs are remembered across both clear history and browser + dh = new ClearHistoryDialogHelper({ mode: "browser" }); + dh.onload = function () { + this.validateCheckbox("cookiesAndStorage", true); + this.validateCheckbox("siteSettings", false); + this.validateCheckbox("cache", false); + + this.cancelDialog(); + }; + dh.open(); + await dh.promiseClosed; +}); + /** * Ensures that the "Everything" duration option works. */ @@ -681,7 +345,7 @@ add_task(async function test_everything() { let promiseSanitized = promiseSanitizationComplete(); await PlacesTestUtils.addVisits(places); - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { is( this.isWarningPanelVisible(), @@ -728,7 +392,7 @@ add_task(async function test_everything_warning() { let promiseSanitized = promiseSanitizationComplete(); await PlacesTestUtils.addVisits(places); - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { is( this.isWarningPanelVisible(), @@ -761,7 +425,7 @@ add_task(async function test_everything_warning() { * and enabled when at least one checkbox is checked */ add_task(async function testAcceptButtonDisabled() { - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = async function () { let clearButton = this.win.document .querySelector("dialog") @@ -769,12 +433,6 @@ add_task(async function testAcceptButtonDisabled() { this.uncheckAllCheckboxes(); await new Promise(resolve => SimpleTest.executeSoon(resolve)); is(clearButton.disabled, true, "Clear button should be disabled"); - // await BrowserTestUtils.waitForMutationCondition( - // clearButton, - // { attributes: true }, - // () => clearButton.disabled, - // "Clear button should be disabled" - // ); this.checkPrefCheckbox("cache", true); await new Promise(resolve => SimpleTest.executeSoon(resolve)); @@ -790,7 +448,7 @@ add_task(async function testAcceptButtonDisabled() { * Tests to see if the warning box is hidden when opened in the clear on shutdown context */ add_task(async function testWarningBoxInClearOnShutdown() { - let dh = new DialogHelper("clearSiteData"); + let dh = new ClearHistoryDialogHelper({ mode: "clearSiteData" }); dh.onload = function () { this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); is( @@ -803,7 +461,7 @@ add_task(async function testWarningBoxInClearOnShutdown() { dh.open(); await dh.promiseClosed; - dh = new DialogHelper("clearOnShutdown"); + dh = new ClearHistoryDialogHelper({ mode: "clearOnShutdown" }); dh.onload = function () { is( BrowserTestUtils.isVisible(this.getWarningPanel()), @@ -853,7 +511,7 @@ add_task(async function test_history_downloads_checked() { await PlacesTestUtils.addVisits(places); - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { this.selectDuration(Sanitizer.TIMESPAN_HOUR); this.checkPrefCheckbox("historyFormDataAndDownloads", true); @@ -905,7 +563,7 @@ add_task(async function test_cannot_clear_history() { }); let uris = [pURI]; - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { var cb = this.win.document.querySelectorAll( "checkbox[id='historyFormDataAndDownloads']" @@ -932,7 +590,7 @@ add_task(async function test_cannot_clear_history() { add_task(async function test_no_formdata_history_to_clear() { let promiseSanitized = promiseSanitizationComplete(); - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { var cb = this.win.document.querySelectorAll( "checkbox[id='historyFormDataAndDownloads']" @@ -955,7 +613,7 @@ add_task(async function test_form_entries() { let promiseSanitized = promiseSanitizationComplete(); - let dh = new DialogHelper(); + let dh = new ClearHistoryDialogHelper(); dh.onload = function () { var cb = this.win.document.querySelectorAll( "checkbox[id='historyFormDataAndDownloads']" @@ -975,97 +633,13 @@ add_task(async function test_form_entries() { await dh.promiseClosed; }); -add_task(async function test_cookie_sizes() { - await clearAndValidateDataSizes({ - clearCookies: true, - clearCache: false, - clearDownloads: false, - timespan: Sanitizer.TIMESPAN_HOUR, - }); - await clearAndValidateDataSizes({ - clearCookies: true, - clearCache: false, - clearDownloads: false, - timespan: Sanitizer.TIMESPAN_4HOURS, - }); - await clearAndValidateDataSizes({ - clearCookies: true, - clearCache: false, - clearDownloads: false, - timespan: Sanitizer.TIMESPAN_EVERYTHING, - }); -}); - -add_task(async function test_cache_sizes() { - await clearAndValidateDataSizes({ - clearCookies: false, - clearCache: true, - clearDownloads: false, - timespan: Sanitizer.TIMESPAN_HOUR, - }); - await clearAndValidateDataSizes({ - clearCookies: false, - clearCache: true, - clearDownloads: false, - timespan: Sanitizer.TIMESPAN_4HOURS, - }); - await clearAndValidateDataSizes({ - clearCookies: false, - clearCache: true, - clearDownloads: false, - timespan: Sanitizer.TIMESPAN_EVERYTHING, - }); -}); - -add_task(async function test_downloads_sizes() { - await clearAndValidateDataSizes({ - clearCookies: false, - clearCache: false, - clearDownloads: true, - timespan: Sanitizer.TIMESPAN_HOUR, - }); - await clearAndValidateDataSizes({ - clearCookies: false, - clearCache: false, - clearDownloads: true, - timespan: Sanitizer.TIMESPAN_4HOURS, - }); - await clearAndValidateDataSizes({ - clearCookies: false, - clearCache: false, - clearDownloads: true, - timespan: Sanitizer.TIMESPAN_EVERYTHING, - }); -}); - -add_task(async function test_all_data_sizes() { - await clearAndValidateDataSizes({ - clearCookies: true, - clearCache: true, - clearDownloads: true, - timespan: Sanitizer.TIMESPAN_HOUR, - }); - await clearAndValidateDataSizes({ - clearCookies: true, - clearCache: true, - clearDownloads: true, - timespan: Sanitizer.TIMESPAN_4HOURS, - }); - await clearAndValidateDataSizes({ - clearCookies: true, - clearCache: true, - clearDownloads: true, - timespan: Sanitizer.TIMESPAN_EVERYTHING, - }); -}); - // test the case when we open the dialog through the clear on shutdown settings add_task(async function test_clear_on_shutdown() { await SpecialPowers.pushPrefEnv({ set: [["privacy.sanitize.sanitizeOnShutdown", true]], }); - let dh = new DialogHelper("clearOnShutdown"); + let dh = new ClearHistoryDialogHelper({ mode: "clearOnShutdown" }); dh.onload = async function () { this.uncheckAllCheckboxes(); this.checkPrefCheckbox("historyFormDataAndDownloads", false); @@ -1134,7 +708,7 @@ add_task(async function test_clear_on_shutdown() { await ensureDownloadsClearedState(downloadIDs, false); await ensureDownloadsClearedState(olderDownloadIDs, false); - dh = new DialogHelper("clearOnShutdown"); + dh = new ClearHistoryDialogHelper({ mode: "clearOnShutdown" }); dh.onload = async function () { this.uncheckAllCheckboxes(); this.checkPrefCheckbox("historyFormDataAndDownloads", true); @@ -1192,89 +766,6 @@ add_task(async function test_clear_on_shutdown() { await SiteDataTestUtils.clear(); }); -// test default prefs for entry points -add_task(async function test_defaults_prefs() { - let dh = new DialogHelper("clearSiteData"); - dh.onload = function () { - this.validateCheckbox("historyFormDataAndDownloads", false); - this.validateCheckbox("cache", true); - this.validateCheckbox("cookiesAndStorage", true); - this.validateCheckbox("siteSettings", false); - - this.cancelDialog(); - }; - dh.open(); - await dh.promiseClosed; - - // We don't need to specify the mode again, - // as the default mode is taken (browser, clear history) - - dh = new DialogHelper(); - dh.onload = function () { - // Default checked for browser and clear history mode - this.validateCheckbox("historyFormDataAndDownloads", true); - this.validateCheckbox("cache", true); - this.validateCheckbox("cookiesAndStorage", true); - this.validateCheckbox("siteSettings", false); - - this.cancelDialog(); - }; - dh.open(); - await dh.promiseClosed; -}); - -/** - * Helper function to simulate switching timespan selections and - * validate data sizes before and after clearing - * - * @param {Object} - * clearCookies - boolean - * clearDownloads - boolean - * clearCaches - boolean - * timespan - one of Sanitizer.TIMESPAN_* - */ -async function clearAndValidateDataSizes({ - clearCache, - clearDownloads, - clearCookies, - timespan, -}) { - await blankSlate(); - - await addToDownloadList(); - await addToSiteUsage(); - let promiseSanitized = promiseSanitizationComplete(); - - await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); - - let dh = new DialogHelper(); - dh.onload = async function () { - await validateDataSizes(this); - this.checkPrefCheckbox("cache", clearCache); - this.checkPrefCheckbox("cookiesAndStorage", clearCookies); - this.checkPrefCheckbox("historyFormDataAndDownloads", clearDownloads); - this.selectDuration(timespan); - this.acceptDialog(); - }; - dh.onunload = async function () { - await promiseSanitized; - }; - dh.open(); - await dh.promiseClosed; - - let dh2 = new DialogHelper(); - // Check if the newly cleared values are reflected - dh2.onload = async function () { - await validateDataSizes(this); - this.acceptDialog(); - }; - dh2.open(); - await dh2.promiseClosed; - - await SiteDataTestUtils.clear(); - BrowserTestUtils.removeTab(gBrowser.selectedTab); -} - add_task(async function testEntryPointTelemetry() { Services.fog.testResetFOG(); @@ -1401,27 +892,47 @@ add_task(async function testClearingOptionsTelemetry() { ); }); -add_task(async function testCheckboxStatesAfterMigration() { +add_task(async function testClearHistoryCheckboxStatesAfterMigration() { await SpecialPowers.pushPrefEnv({ set: [ - ["privacy.clearOnShutdown.history", false], - ["privacy.clearOnShutdown.formdata", true], - ["privacy.clearOnShutdown.cookies", true], - ["privacy.clearOnShutdown.offlineApps", false], - ["privacy.clearOnShutdown.sessions", false], - ["privacy.clearOnShutdown.siteSettings", false], - ["privacy.clearOnShutdown.cache", true], - ["privacy.clearOnShutdown_v2.cookiesAndStorage", false], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", false], + ["privacy.cpd.history", false], + ["privacy.cpd.formdata", true], + ["privacy.cpd.cookies", true], + ["privacy.cpd.offlineApps", false], + ["privacy.cpd.sessions", false], + ["privacy.cpd.siteSettings", false], + ["privacy.cpd.cache", true], + // Set cookiesAndStorage to verify that the pref is flipped in the test + ["privacy.clearHistory.cookiesAndStorage", false], + ["privacy.sanitize.cpd.hasMigratedToNewPrefs", false], ], }); - let dh = new DialogHelper("clearOnShutdown"); + let dh = new ClearHistoryDialogHelper({ mode: "clearHistory" }); dh.onload = function () { this.validateCheckbox("cookiesAndStorage", true); this.validateCheckbox("historyFormDataAndDownloads", false); this.validateCheckbox("cache", true); this.validateCheckbox("siteSettings", false); + + this.checkPrefCheckbox("siteSettings", true); + this.checkPrefCheckbox("cookiesAndStorage", false); + this.acceptDialog(); + }; + dh.open(); + await dh.promiseClosed; + + is( + Services.prefs.getBoolPref("privacy.sanitize.cpd.hasMigratedToNewPrefs"), + true, + "Migration is complete for cpd branch" + ); + + // make sure the migration doesn't run again + dh = new ClearHistoryDialogHelper({ mode: "clearHistory" }); + dh.onload = function () { + this.validateCheckbox("siteSettings", true); + this.validateCheckbox("cookiesAndStorage", false); this.cancelDialog(); }; dh.open(); diff --git a/browser/base/content/test/sanitize/browser_sanitizeDialog_v2_dataSizes.js b/browser/base/content/test/sanitize/browser_sanitizeDialog_v2_dataSizes.js new file mode 100644 index 0000000000..ccb3c7d519 --- /dev/null +++ b/browser/base/content/test/sanitize/browser_sanitizeDialog_v2_dataSizes.js @@ -0,0 +1,310 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +/** + * This tests the new clear history dialog's data size display functionality + */ +ChromeUtils.defineESModuleGetters(this, { + sinon: "resource://testing-common/Sinon.sys.mjs", + Sanitizer: "resource:///modules/Sanitizer.sys.mjs", +}); + +add_setup(async function () { + await blankSlate(); + registerCleanupFunction(async function () { + await blankSlate(); + await PlacesTestUtils.promiseAsyncUpdates(); + }); + await SpecialPowers.pushPrefEnv({ + set: [["privacy.sanitize.useOldClearHistoryDialog", false]], + }); +}); + +/** + * Helper function to validate the data sizes shown for each time selection + * + * @param {ClearHistoryDialogHelper} dh - dialog object to access window and timespan + */ +async function validateDataSizes(ClearHistoryDialogHelper) { + let timespans = [ + "TIMESPAN_HOUR", + "TIMESPAN_2HOURS", + "TIMESPAN_4HOURS", + "TIMESPAN_TODAY", + "TIMESPAN_EVERYTHING", + ]; + + // get current data sizes from siteDataManager + let cacheUsage = await SiteDataManager.getCacheSize(); + let quotaUsage = await SiteDataManager.getQuotaUsageForTimeRanges(timespans); + + for (let i = 0; i < timespans.length; i++) { + // select timespan to check + ClearHistoryDialogHelper.selectDuration(Sanitizer[timespans[i]]); + + // get the elements + let clearCookiesAndSiteDataCheckbox = + ClearHistoryDialogHelper.win.document.getElementById("cookiesAndStorage"); + let clearCacheCheckbox = + ClearHistoryDialogHelper.win.document.getElementById("cache"); + + let [convertedQuotaUsage] = DownloadUtils.convertByteUnits( + quotaUsage[timespans[i]] + ); + let [, convertedCacheUnit] = DownloadUtils.convertByteUnits(cacheUsage); + + // Ensure l10n is finished before inspecting the category labels. + await ClearHistoryDialogHelper.win.document.l10n.translateElements([ + clearCookiesAndSiteDataCheckbox, + clearCacheCheckbox, + ]); + ok( + clearCacheCheckbox.label.includes(convertedCacheUnit), + "Should show the cache usage" + ); + ok( + clearCookiesAndSiteDataCheckbox.label.includes(convertedQuotaUsage), + `Should show the quota usage as ${convertedQuotaUsage}` + ); + } +} + +/** + * Helper function to simulate switching timespan selections and + * validate data sizes before and after clearing + * + * @param {Object} + * clearCookies - boolean + * clearDownloads - boolean + * clearCaches - boolean + * timespan - one of Sanitizer.TIMESPAN_* + */ +async function clearAndValidateDataSizes({ + clearCache, + clearDownloads, + clearCookies, + timespan, +}) { + await blankSlate(); + + await addToSiteUsage(); + let promiseSanitized = promiseSanitizationComplete(); + + await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); + + let dh = new ClearHistoryDialogHelper({ checkingDataSizes: true }); + dh.onload = async function () { + await validateDataSizes(this); + this.checkPrefCheckbox("cache", clearCache); + this.checkPrefCheckbox("cookiesAndStorage", clearCookies); + this.checkPrefCheckbox("historyFormDataAndDownloads", clearDownloads); + this.selectDuration(timespan); + this.acceptDialog(); + }; + dh.onunload = async function () { + await promiseSanitized; + }; + dh.open(); + await dh.promiseClosed; + + let dh2 = new ClearHistoryDialogHelper({ checkingDataSizes: true }); + // Check if the newly cleared values are reflected + dh2.onload = async function () { + await validateDataSizes(this); + this.acceptDialog(); + }; + dh2.open(); + await dh2.promiseClosed; + + await SiteDataTestUtils.clear(); + BrowserTestUtils.removeTab(gBrowser.selectedTab); +} + +add_task(async function test_cookie_sizes() { + await clearAndValidateDataSizes({ + clearCookies: true, + clearCache: false, + clearDownloads: false, + timespan: Sanitizer.TIMESPAN_HOUR, + }); + await clearAndValidateDataSizes({ + clearCookies: true, + clearCache: false, + clearDownloads: false, + timespan: Sanitizer.TIMESPAN_4HOURS, + }); + await clearAndValidateDataSizes({ + clearCookies: true, + clearCache: false, + clearDownloads: false, + timespan: Sanitizer.TIMESPAN_EVERYTHING, + }); +}); + +add_task(async function test_cache_sizes() { + await clearAndValidateDataSizes({ + clearCookies: false, + clearCache: true, + clearDownloads: false, + timespan: Sanitizer.TIMESPAN_HOUR, + }); + await clearAndValidateDataSizes({ + clearCookies: false, + clearCache: true, + clearDownloads: false, + timespan: Sanitizer.TIMESPAN_4HOURS, + }); + await clearAndValidateDataSizes({ + clearCookies: false, + clearCache: true, + clearDownloads: false, + timespan: Sanitizer.TIMESPAN_EVERYTHING, + }); +}); + +add_task(async function test_all_data_sizes() { + await clearAndValidateDataSizes({ + clearCookies: true, + clearCache: true, + clearDownloads: true, + timespan: Sanitizer.TIMESPAN_HOUR, + }); + await clearAndValidateDataSizes({ + clearCookies: true, + clearCache: true, + clearDownloads: true, + timespan: Sanitizer.TIMESPAN_4HOURS, + }); + await clearAndValidateDataSizes({ + clearCookies: true, + clearCache: true, + clearDownloads: true, + timespan: Sanitizer.TIMESPAN_EVERYTHING, + }); +}); + +// This test makes sure that the user can change their timerange option +// even if the data sizes are not loaded yet. +add_task(async function testUIWithDataSizesLoading() { + await blankSlate(); + await addToSiteUsage(); + + let origGetQuotaUsageForTimeRanges = + SiteDataManager.getQuotaUsageForTimeRanges.bind(SiteDataManager); + let resolveStubFn; + let resolverAssigned = false; + + let dh = new ClearHistoryDialogHelper(); + // Create a sandbox for isolated stubbing within the test + let sandbox = sinon.createSandbox(); + sandbox + .stub(SiteDataManager, "getQuotaUsageForTimeRanges") + .callsFake(async (...args) => { + info("stub called"); + + let dataSizesReadyToLoadPromise = new Promise(resolve => { + resolveStubFn = resolve; + info("Sending message to notify dialog that the resolver is assigned"); + window.postMessage("resolver-assigned", "*"); + resolverAssigned = true; + }); + await dataSizesReadyToLoadPromise; + return origGetQuotaUsageForTimeRanges(...args); + }); + dh.onload = async function () { + // we add this event listener in the case where init finishes before the resolver is assigned + if (!resolverAssigned) { + await new Promise(resolve => { + let listener = event => { + if (event.data === "resolver-assigned") { + window.removeEventListener("message", listener); + // we are ready to test the dialog without any data sizes loaded + resolve(); + } + }; + window.addEventListener("message", listener); + }); + } + + ok( + !this.win.gSanitizePromptDialog._dataSizesUpdated, + "Data sizes should not have loaded yet" + ); + this.selectDuration(Sanitizer.TIMESPAN_2HOURS); + + info("triggering loading state end"); + resolveStubFn(); + + await this.win.gSanitizePromptDialog.dataSizesFinishedUpdatingPromise; + + validateDataSizes(this); + this.cancelDialog(); + }; + dh.open(); + await dh.promiseClosed; + + // Restore the sandbox after the test is complete + sandbox.restore(); +}); + +add_task(async function testClearingBeforeDataSizesLoad() { + await blankSlate(); + await addToSiteUsage(); + + // add site data that we can verify if it gets cleared + await createDummyDataForHost("example.org"); + await createDummyDataForHost("example.com"); + + ok( + await SiteDataTestUtils.hasIndexedDB("https://example.org"), + "We have indexedDB data for example.org" + ); + ok( + await SiteDataTestUtils.hasIndexedDB("https://example.com"), + "We have indexedDB data for example.com" + ); + + let dh = new ClearHistoryDialogHelper(); + let promiseSanitized = promiseSanitizationComplete(); + // Create a sandbox for isolated stubbing within the test + let sandbox = sinon.createSandbox(); + sandbox + .stub(SiteDataManager, "getQuotaUsageForTimeRanges") + .callsFake(async () => { + info("stub called"); + + info("This promise should never resolve"); + await new Promise(resolve => {}); + }); + dh.onload = async function () { + // we don't need to initiate a event listener to wait for the resolver to be assigned for this + // test since we do not want the data sizes to load + ok( + !this.win.gSanitizePromptDialog._dataSizesUpdated, + "Data sizes should not be loaded yet" + ); + this.selectDuration(Sanitizer.TIMESPAN_2HOURS); + this.checkPrefCheckbox("cookiesAndStorage", true); + this.acceptDialog(); + }; + dh.onunload = async () => { + await promiseSanitized; + }; + dh.open(); + await dh.promiseClosed; + + // Data for example.org should be cleared + ok( + !(await SiteDataTestUtils.hasIndexedDB("https://example.org")), + "We don't have indexedDB data for example.org" + ); + // Data for example.com should be cleared + ok( + !(await SiteDataTestUtils.hasIndexedDB("https://example.com")), + "We don't have indexedDB data for example.com" + ); + + // Restore the sandbox after the test is complete + sandbox.restore(); +}); diff --git a/browser/base/content/test/sanitize/browser_sanitizeOnShutdown_migration.js b/browser/base/content/test/sanitize/browser_sanitizeOnShutdown_migration.js index 3c2af1d513..bc5c925702 100644 --- a/browser/base/content/test/sanitize/browser_sanitizeOnShutdown_migration.js +++ b/browser/base/content/test/sanitize/browser_sanitizeOnShutdown_migration.js @@ -17,7 +17,7 @@ add_task(async function testMigrationOfCacheAndSiteSettings() { ["privacy.clearOnShutdown.siteSettings", true], ["privacy.clearOnShutdown_v2.cache", false], ["privacy.clearOnShutdown_v2.siteSettings", false], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", false], + ["privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs", false], ], }); @@ -46,7 +46,7 @@ add_task(async function testMigrationOfCacheAndSiteSettings() { Assert.equal( Services.prefs.getBoolPref( - "privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs" + "privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs" ), true, "migration pref has been flipped" @@ -59,7 +59,7 @@ add_task(async function testHistoryAndFormData_historyTrue() { ["privacy.clearOnShutdown.history", true], ["privacy.clearOnShutdown.formdata", false], ["privacy.clearOnShutdown_v2.historyFormDataAndDownloads", false], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", false], + ["privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs", false], ], }); @@ -85,7 +85,7 @@ add_task(async function testHistoryAndFormData_historyTrue() { Assert.equal( Services.prefs.getBoolPref( - "privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs" + "privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs" ), true, "migration pref has been flipped" @@ -98,7 +98,7 @@ add_task(async function testHistoryAndFormData_historyFalse() { ["privacy.clearOnShutdown.history", false], ["privacy.clearOnShutdown.formdata", true], ["privacy.clearOnShutdown_v2.historyFormDataAndDownloads", true], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", false], + ["privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs", false], ], }); @@ -124,7 +124,7 @@ add_task(async function testHistoryAndFormData_historyFalse() { Assert.equal( Services.prefs.getBoolPref( - "privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs" + "privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs" ), true, "migration pref has been flipped" @@ -138,7 +138,7 @@ add_task(async function testCookiesAndStorage_cookiesFalse() { ["privacy.clearOnShutdown.offlineApps", true], ["privacy.clearOnShutdown.sessions", true], ["privacy.clearOnShutdown_v2.cookiesAndStorage", true], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", false], + ["privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs", false], ], }); @@ -168,7 +168,7 @@ add_task(async function testCookiesAndStorage_cookiesFalse() { Assert.equal( Services.prefs.getBoolPref( - "privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs" + "privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs" ), true, "migration pref has been flipped" @@ -182,7 +182,7 @@ add_task(async function testCookiesAndStorage_cookiesTrue() { ["privacy.clearOnShutdown.offlineApps", false], ["privacy.clearOnShutdown.sessions", false], ["privacy.clearOnShutdown_v2.cookiesAndStorage", false], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", false], + ["privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs", false], ], }); @@ -211,7 +211,7 @@ add_task(async function testCookiesAndStorage_cookiesTrue() { Assert.equal( Services.prefs.getBoolPref( - "privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs" + "privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs" ), true, "migration pref has been flipped" @@ -225,7 +225,7 @@ add_task(async function testMigrationDoesNotRepeat() { ["privacy.clearOnShutdown.offlineApps", false], ["privacy.clearOnShutdown.sessions", false], ["privacy.clearOnShutdown_v2.cookiesAndStorage", false], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", true], + ["privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs", true], ], }); @@ -255,7 +255,7 @@ add_task(async function testMigrationDoesNotRepeat() { Assert.equal( Services.prefs.getBoolPref( - "privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs" + "privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs" ), true, "migration pref has been flipped" @@ -273,7 +273,7 @@ add_task(async function ensureNoOldPrefsAreEffectedByMigration() { ["privacy.clearOnShutdown.siteSettings", true], ["privacy.clearOnShutdown.cache", true], ["privacy.clearOnShutdown_v2.cookiesAndStorage", false], - ["privacy.sanitize.sanitizeOnShutdown.hasMigratedToNewPrefs", false], + ["privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs", false], ], }); diff --git a/browser/base/content/test/sanitize/head.js b/browser/base/content/test/sanitize/head.js index f5a6031b84..30d96c69f6 100644 --- a/browser/base/content/test/sanitize/head.js +++ b/browser/base/content/test/sanitize/head.js @@ -3,10 +3,34 @@ ChromeUtils.defineESModuleGetters(this, { FormHistory: "resource://gre/modules/FormHistory.sys.mjs", PermissionTestUtils: "resource://testing-common/PermissionTestUtils.sys.mjs", PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", + PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs", + FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs", Sanitizer: "resource:///modules/Sanitizer.sys.mjs", SiteDataTestUtils: "resource://testing-common/SiteDataTestUtils.sys.mjs", }); +const kMsecPerMin = 60 * 1000; +const kUsecPerMin = kMsecPerMin * 1000; +let today = Date.now() - new Date().setHours(0, 0, 0, 0); +let nowMSec = Date.now(); +let nowUSec = nowMSec * 1000; +const TEST_TARGET_FILE_NAME = "test-download.txt"; +const TEST_QUOTA_USAGE_HOST = "example.com"; +const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST; +const TEST_QUOTA_USAGE_URL = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + TEST_QUOTA_USAGE_ORIGIN + ) + "site_data_test.html"; +const SITE_ORIGINS = [ + "https://www.example.com", + "https://example.org", + "http://localhost:8000", + "http://localhost:3000", +]; + +let fileURL; + function createIndexedDB(host, originAttributes) { let uri = Services.io.newURI("https://" + host); let principal = Services.scriptSecurityManager.createContentPrincipal( @@ -369,3 +393,345 @@ async function createDummyDataForHost(host) { await SiteDataTestUtils.addToIndexedDB(origin); await SiteDataTestUtils.addServiceWorker(dummySWURL); } + +/** + * Helper function to create file URL to open + * + * @returns {Object} a file URL + */ +function createFileURL() { + if (!fileURL) { + let file = Services.dirsvc.get("TmpD", Ci.nsIFile); + file.append("foo.txt"); + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); + + fileURL = Services.io.newFileURI(file); + } + return fileURL; +} + +/** + * Removes all history visits, downloads, and form entries. + */ +async function blankSlate() { + let publicList = await Downloads.getList(Downloads.PUBLIC); + let downloads = await publicList.getAll(); + for (let download of downloads) { + await publicList.remove(download); + await download.finalize(true); + } + + await FormHistory.update({ op: "remove" }); + await PlacesUtils.history.clear(); +} + +/** + * Adds multiple downloads to the PUBLIC download list + */ +async function addToDownloadList() { + const url = createFileURL(); + const downloadsList = await Downloads.getList(Downloads.PUBLIC); + let timeOptions = [1, 2, 4, 24, 128, 128]; + let buffer = 100000; + + for (let i = 0; i < timeOptions.length; i++) { + let timeDownloaded = 60 * kMsecPerMin * timeOptions[i]; + if (timeOptions[i] === 24) { + timeDownloaded = today; + } + + let download = await Downloads.createDownload({ + source: { url: url.spec, isPrivate: false }, + target: { path: FileTestUtils.getTempFile(TEST_TARGET_FILE_NAME).path }, + startTime: { + getTime: _ => { + return nowMSec - timeDownloaded + buffer; + }, + }, + }); + + Assert.ok(!!download); + downloadsList.add(download); + } + let items = await downloadsList.getAll(); + Assert.equal(items.length, 6, "Items were added to the list"); +} + +async function addToSiteUsage() { + // Fill indexedDB with test data. + // Don't wait for the page to load, to register the content event handler as quickly as possible. + // If this test goes intermittent, we might have to tell the page to wait longer before + // firing the event. + BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL, false); + await BrowserTestUtils.waitForContentEvent( + gBrowser.selectedBrowser, + "test-indexedDB-done", + false, + null, + true + ); + BrowserTestUtils.removeTab(gBrowser.selectedTab); + + let siteLastAccessed = [1, 2, 4, 24]; + + let staticUsage = 4096 * 6; + // Add a time buffer so the site access falls within the time range + const buffer = 10000; + + // Change lastAccessed of sites + for (let index = 0; index < siteLastAccessed.length; index++) { + let lastAccessedTime = 60 * kMsecPerMin * siteLastAccessed[index]; + if (siteLastAccessed[index] === 24) { + lastAccessedTime = today; + } + + let site = SiteDataManager._testInsertSite(SITE_ORIGINS[index], { + quotaUsage: staticUsage, + lastAccessed: (nowMSec - lastAccessedTime + buffer) * 1000, + }); + Assert.ok(site, "Site added successfully"); + } +} + +function promiseSanitizationComplete() { + return TestUtils.topicObserved("sanitizer-sanitization-complete"); +} + +/** + * This wraps the dialog and provides some convenience methods for interacting + * with it. + * + * @param {Window} browserWin (optional) + * The browser window that the dialog is expected to open in. If not + * supplied, the initial browser window of the test run is used. + * @param {Object} {mode, checkingDataSizes} + * mode: context to open the dialog in + * One of + * clear on shutdown settings context ("clearOnShutdown"), + * clear site data settings context ("clearSiteData"), + * clear history context ("clearHistory"), + * browser context ("browser") + * "browser" by default + * checkingDataSizes: boolean check if we should wait for the data sizes + * to load + * + */ +function ClearHistoryDialogHelper({ + mode = "browser", + checkingDataSizes = false, +} = {}) { + this._browserWin = window; + this.win = null; + this._mode = mode; + this._checkingDataSizes = checkingDataSizes; + this.promiseClosed = new Promise(resolve => { + this._resolveClosed = resolve; + }); +} + +ClearHistoryDialogHelper.prototype = { + /** + * "Presses" the dialog's OK button. + */ + acceptDialog() { + let dialogEl = this.win.document.querySelector("dialog"); + is( + dialogEl.getButton("accept").disabled, + false, + "Dialog's OK button should not be disabled" + ); + dialogEl.acceptDialog(); + }, + + /** + * "Presses" the dialog's Cancel button. + */ + cancelDialog() { + this.win.document.querySelector("dialog").cancelDialog(); + }, + + /** + * (Un)checks a history scope checkbox (browser & download history, + * form history, etc.). + * + * @param aPrefName + * The final portion of the checkbox's privacy.cpd.* preference name + * @param aCheckState + * True if the checkbox should be checked, false otherwise + */ + checkPrefCheckbox(aPrefName, aCheckState) { + var cb = this.win.document.querySelectorAll( + "checkbox[id='" + aPrefName + "']" + ); + is(cb.length, 1, "found checkbox for " + aPrefName + " id"); + if (cb[0].checked != aCheckState) { + cb[0].click(); + } + }, + + /** + * @param {String} aCheckboxId + * The checkbox id name + * @param {Boolean} aCheckState + * True if the checkbox should be checked, false otherwise + */ + validateCheckbox(aCheckboxId, aCheckState) { + let cb = this.win.document.querySelectorAll( + "checkbox[id='" + aCheckboxId + "']" + ); + is(cb.length, 1, `found checkbox for id=${aCheckboxId}`); + is( + cb[0].checked, + aCheckState, + `checkbox for ${aCheckboxId} is ${aCheckState}` + ); + }, + + /** + * Makes sure all the checkboxes are checked. + */ + _checkAllCheckboxesCustom(check) { + var cb = this.win.document.querySelectorAll(".clearingItemCheckbox"); + ok(cb.length, "found checkboxes for ids"); + for (var i = 0; i < cb.length; ++i) { + if (cb[i].checked != check) { + cb[i].click(); + } + } + }, + + checkAllCheckboxes() { + this._checkAllCheckboxesCustom(true); + }, + + uncheckAllCheckboxes() { + this._checkAllCheckboxesCustom(false); + }, + + /** + * @return The dialog's duration dropdown + */ + getDurationDropdown() { + return this.win.document.getElementById("sanitizeDurationChoice"); + }, + + /** + * @return The clear-everything warning box + */ + getWarningPanel() { + return this.win.document.getElementById("sanitizeEverythingWarningBox"); + }, + + /** + * @return True if the "Everything" warning panel is visible (as opposed to + * the tree) + */ + isWarningPanelVisible() { + return !this.getWarningPanel().hidden; + }, + + /** + * Opens the clear recent history dialog. Before calling this, set + * this.onload to a function to execute onload. It should close the dialog + * when done so that the tests may continue. Set this.onunload to a function + * to execute onunload. this.onunload is optional. If it returns true, the + * caller is expected to call promiseAsyncUpdates at some point; if false is + * returned, promiseAsyncUpdates is called automatically. + */ + async open() { + let dialogPromise = BrowserTestUtils.promiseAlertDialogOpen( + null, + "chrome://browser/content/sanitize_v2.xhtml", + { + isSubDialog: true, + } + ); + + // We want to simulate opening the dialog inside preferences for clear history + // and clear site data + if (this._mode != "browser") { + await openPreferencesViaOpenPreferencesAPI("privacy", { + leaveOpen: true, + }); + let tabWindow = gBrowser.selectedBrowser.contentWindow; + let clearDialogOpenButtonId = this._mode + "Button"; + // the id for clear on shutdown is of a different format + if (this._mode == "clearOnShutdown") { + // set always clear to true to enable the clear on shutdown dialog + let enableSettingsCheckbox = + tabWindow.document.getElementById("alwaysClear"); + if (!enableSettingsCheckbox.checked) { + enableSettingsCheckbox.click(); + } + clearDialogOpenButtonId = "clearDataSettings"; + } + // open dialog + tabWindow.document.getElementById(clearDialogOpenButtonId).click(); + } + // We open the dialog in the chrome context in other cases + else { + executeSoon(() => { + Sanitizer.showUI(this._browserWin, this._mode); + }); + } + + this.win = await dialogPromise; + this.win.addEventListener( + "load", + () => { + // Run onload on next tick so that gSanitizePromptDialog.init can run first. + executeSoon(async () => { + if (this._checkingDataSizes) { + // we wait for the data sizes to load to avoid async errors when validating sizes + await this.win.gSanitizePromptDialog + .dataSizesFinishedUpdatingPromise; + } + this.onload(); + }); + }, + { once: true } + ); + this.win.addEventListener( + "unload", + () => { + // Some exceptions that reach here don't reach the test harness, but + // ok()/is() do... + (async () => { + if (this.onunload) { + await this.onunload(); + } + if (this._mode != "browser") { + BrowserTestUtils.removeTab(gBrowser.selectedTab); + } + await PlacesTestUtils.promiseAsyncUpdates(); + this._resolveClosed(); + this.win = null; + })(); + }, + { once: true } + ); + }, + + /** + * Selects a duration in the duration dropdown. + * + * @param aDurVal + * One of the Sanitizer.TIMESPAN_* values + */ + selectDuration(aDurVal) { + this.getDurationDropdown().value = aDurVal; + if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) { + is( + this.isWarningPanelVisible(), + true, + "Warning panel should be visible for TIMESPAN_EVERYTHING" + ); + } else { + is( + this.isWarningPanelVisible(), + false, + "Warning panel should not be visible for non-TIMESPAN_EVERYTHING" + ); + } + }, +}; diff --git a/browser/base/content/test/siteIdentity/browser.toml b/browser/base/content/test/siteIdentity/browser.toml index f0b6191302..c075052fa5 100644 --- a/browser/base/content/test/siteIdentity/browser.toml +++ b/browser/base/content/test/siteIdentity/browser.toml @@ -106,6 +106,12 @@ skip-if = [ ["browser_identity_UI.js"] https_first_disabled = true +["browser_identity_web_controlled_blank.js"] +support-files = [ + "test_web_controlled_blank.html", + "dummy_page.html", +] + ["browser_iframe_navigation.js"] https_first_disabled = true support-files = ["iframe_navigation.html"] diff --git a/browser/base/content/test/siteIdentity/browser_identity_web_controlled_blank.js b/browser/base/content/test/siteIdentity/browser_identity_web_controlled_blank.js new file mode 100644 index 0000000000..01eca12178 --- /dev/null +++ b/browser/base/content/test/siteIdentity/browser_identity_web_controlled_blank.js @@ -0,0 +1,128 @@ +/* + * This work is marked with CC0 1.0. To view a copy of this license, + * visit http://creativecommons.org/publicdomain/zero/1.0 + * + * + * Tests for correct behaviour of web-controlled about:blank pages in identity panel. + * Getting into a testable state is different enough that we test all the behaviors here + * separately, rather than with the usual (secure, insecure, etc.) cases + */ + +const TEST_HOST = "example.com"; +const TEST_ORIGIN = "https://" + TEST_HOST; +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + TEST_ORIGIN +); +const LOCALHOST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://127.0.0.1:8888" +); +const TEST_URI = TEST_PATH + "test_web_controlled_blank.html"; +const DUMMY_URI = LOCALHOST_PATH + "dummy_page.html"; + +// Open a new tab of `test_web_controlled_blank.html` and click +// an element with a particular ID to open an about:blank popup +// that is controlled by that first tab. +// +// Then test that the UI elements are all correct. And make sure +// we don't have anything odd going on after navigating away in the +// popup. +async function web_controlled_about_blank_helper(id_to_click) { + // Open a new tab that will control about:blank pages + await BrowserTestUtils.withNewTab(TEST_URI, async browser => { + // Open a new popup by clicking the provided id_to_click from the content + let popupWindowPromise = BrowserTestUtils.waitForNewWindow(); + await SpecialPowers.spawn(browser, [id_to_click], async function (id) { + content.document.getElementById(id).click(); + }); + let popupWindow = await popupWindowPromise; + + // Validate the icon in the urlbar + let identityIcon = popupWindow.document.querySelector("#identity-icon"); + let identityIconImageURL = popupWindow + .getComputedStyle(identityIcon) + .getPropertyValue("list-style-image"); + is( + identityIconImageURL, + `url("chrome://global/skin/icons/info.svg")`, + "The identity icon has a correct image url." + ); + + // Open the identity panel + let popupShown = BrowserTestUtils.waitForEvent( + popupWindow, + "popupshown", + true, + event => event.target == popupWindow.gIdentityHandler._identityPopup + ); + popupWindow.gIdentityHandler._identityIconBox.click(); + info("Waiting for the Control Center to be shown"); + await popupShown; + ok( + !popupWindow.gIdentityHandler._identityPopup.hidden, + "Control Center is visible" + ); + + // Validate that the predecessor is shown in the identity panel + ok( + popupWindow.gIdentityHandler._identityPopupMainViewHeaderLabel.textContent.includes( + TEST_HOST + ), + "Identity UI header shows the host of the predecessor" + ); + + // Validate that the correct message is displayed + is( + popupWindow.gIdentityHandler._identityPopup.getAttribute("connection"), + "associated", + "Identity UI shows associated message." + ); + + // Validate that there is no additional security info + let securityButton = popupWindow.gBrowser.ownerDocument.querySelector( + "#identity-popup-security-button" + ); + is( + securityButton.disabled, + true, + "Security button has correct disabled state" + ); + + // Navigate away to a localhost page and make sure the identity icon changes + let loaded = BrowserTestUtils.browserLoaded( + popupWindow.gBrowser.selectedBrowser, + false, + DUMMY_URI + ); + await SpecialPowers.spawn( + popupWindow.gBrowser.selectedBrowser, + [DUMMY_URI], + async function (uri) { + content.location = uri; + } + ); + info("Waiting for the navigation to a dummy page to complete."); + await loaded; + + identityIconImageURL = popupWindow.gBrowser.ownerGlobal + .getComputedStyle(identityIcon) + .getPropertyValue("list-style-image"); + is( + identityIconImageURL, + `url("chrome://global/skin/icons/page-portrait.svg")`, + "The identity icon has a correct image url after navigating away." + ); + + // Exit this test, cleaning up as we go. + await BrowserTestUtils.closeWindow(popupWindow); + }); +} + +add_task(async function test_document_write() { + await web_controlled_about_blank_helper("document_write"); +}); + +add_task(async function test_innerHTML() { + await web_controlled_about_blank_helper("innerhtml"); +}); diff --git a/browser/base/content/test/siteIdentity/test_web_controlled_blank.html b/browser/base/content/test/siteIdentity/test_web_controlled_blank.html new file mode 100644 index 0000000000..35c1fd4ca2 --- /dev/null +++ b/browser/base/content/test/siteIdentity/test_web_controlled_blank.html @@ -0,0 +1,36 @@ +<!doctype html> +<html> + <head> + <title>Bug 1813463: Web controlled about:blank</title> + </head> + <!-- + This work is marked with CC0 1.0. To view a copy of this license, + visit http://creativecommons.org/publicdomain/zero/1.0 + --> + <body> + <p><a href="#" id="document_write">document.write example</a></p> + <p><a href="#" id="innerhtml">innerHTML example</a></p> + + <script> + document.getElementById("document_write").addEventListener("click", function (aEvent) { + aEvent.preventDefault(); + let popup = window.open("about:blank", "_blank", "width=800,height=600,popup"); + popup.document.write(` + <h1>check security info</h1> + <p>initial window.location = ${popup.location}</p> + <script> + document.write("<p>new window.location = " + window.location + "</p>") + <${"/"}script> + `); + }); + document.getElementById("innerhtml").addEventListener("click", function (aEvent) { + aEvent.preventDefault(); + let popup = window.open("about:blank", "_blank", "width=800,height=600,popup"); + popup.stop(); + popup.document.body.innerHTML = ` + <h1>check security info</h1> + <p>window.location = ${popup.location}</p>`; + }); + </script> + </body> +</html> diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js index 6389e59e00..5e83443ec7 100644 --- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -76,6 +76,9 @@ var gExceptionPaths = [ // Localization file added programatically in FeatureCallout.sys.mjs "resource://app/localization/en-US/browser/featureCallout.ftl", + // Localization file added programatically in ContentAnalysis.sys.mjs + "resource://gre/localization/en-US/toolkit/contentanalysis/", + // CSS files are referenced inside JS in an html template "chrome://browser/content/aboutlogins/components/", ]; @@ -277,6 +280,9 @@ var allowlist = [ // find the references) { file: "chrome://browser/content/screenshots/copied-notification.svg" }, + // Bug 1875361 + { file: "chrome://global/content/ml/SummarizerModel.sys.mjs" }, + // toolkit/xre/MacRunFromDmgUtils.mm { file: "resource://gre/localization/en-US/toolkit/global/run-from-dmg.ftl" }, @@ -871,9 +877,6 @@ add_task(async function checkAllTheFiles() { // Wait for all manifest to be parsed await PerfTestHelpers.throttledMapPromises(manifestURIs, parseManifest); - for (let jsm of Components.manager.getComponentJSMs()) { - gReferencesFromCode.set(jsm, null); - } for (let esModule of Components.manager.getComponentESModules()) { gReferencesFromCode.set(esModule, null); } diff --git a/browser/base/content/test/static/browser_misused_characters_in_strings.js b/browser/base/content/test/static/browser_misused_characters_in_strings.js index 4191cc966e..c234f8107e 100644 --- a/browser/base/content/test/static/browser_misused_characters_in_strings.js +++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js @@ -68,6 +68,28 @@ let gExceptionsList = [ key: "MathML_DeprecatedMathVariantWarning", type: "single-quote", }, + // These error messages contain references to the CSP keywords 'unsafe-eval'/'wasm-unsafe-eval', + // and those keywords contain actual single-quotes: https://w3c.github.io/webappsec-csp/#grammardef-keyword-source + { + file: "csp.properties", + key: "CSPEvalScriptViolation", + type: "single-quote", + }, + { + file: "csp.properties", + key: "CSPROEvalScriptViolation", + type: "single-quote", + }, + { + file: "csp.properties", + key: "CSPWasmEvalScriptViolation", + type: "single-quote", + }, + { + file: "csp.properties", + key: "CSPROWasmEvalScriptViolation", + type: "single-quote", + }, ]; /** diff --git a/browser/base/content/test/static/browser_parsable_css.js b/browser/base/content/test/static/browser_parsable_css.js index b34ae7d9c1..602cc5a7e2 100644 --- a/browser/base/content/test/static/browser_parsable_css.js +++ b/browser/base/content/test/static/browser_parsable_css.js @@ -68,24 +68,6 @@ if (!Services.prefs.getBoolPref("layout.css.zoom.enabled")) { }); } -if (!Services.prefs.getBoolPref("layout.css.math-depth.enabled")) { - // mathml.css UA sheet rule for math-depth. - ignoreList.push({ - sourceName: /\b(scrollbars|mathml)\.css$/i, - errorMessage: /Unknown property .*\bmath-depth\b/i, - isFromDevTools: false, - }); -} - -if (!Services.prefs.getBoolPref("layout.css.math-style.enabled")) { - // mathml.css UA sheet rule for math-style. - ignoreList.push({ - sourceName: /(?:res|gre-resources)\/mathml\.css$/i, - errorMessage: /Unknown property .*\bmath-style\b/i, - isFromDevTools: false, - }); -} - if (!Services.prefs.getBoolPref("layout.css.scroll-anchoring.enabled")) { ignoreList.push({ sourceName: /webconsole\.css$/i, diff --git a/browser/base/content/test/tabPrompts/browser_beforeunload_urlbar.js b/browser/base/content/test/tabPrompts/browser_beforeunload_urlbar.js index 52044b5874..1466e9ca86 100644 --- a/browser/base/content/test/tabPrompts/browser_beforeunload_urlbar.js +++ b/browser/base/content/test/tabPrompts/browser_beforeunload_urlbar.js @@ -9,11 +9,6 @@ const TEST_ROOT = getRootDirectory(gTestPath).replace( "http://example.com" ); -const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( - "prompts.contentPromptSubDialog", - false -); - add_task(async function test_beforeunload_stay_clears_urlbar() { await SpecialPowers.pushPrefEnv({ set: [["dom.require_user_interaction_for_beforeunload", false]], @@ -27,27 +22,10 @@ add_task(async function test_beforeunload_stay_clears_urlbar() { gURLBar.value = inputValue.slice(0, -1); EventUtils.sendString(inputValue.slice(-1)); - if (CONTENT_PROMPT_SUBDIALOG) { - let promptOpenedPromise = - BrowserTestUtils.promiseAlertDialogOpen("cancel"); - EventUtils.synthesizeKey("VK_RETURN"); - await promptOpenedPromise; - await TestUtils.waitForTick(); - } else { - let promptOpenedPromise = TestUtils.topicObserved( - "tabmodal-dialog-loaded" - ); - EventUtils.synthesizeKey("VK_RETURN"); - await promptOpenedPromise; - let promptElement = browser.parentNode.querySelector("tabmodalprompt"); - - // Click the cancel button - promptElement.querySelector(".tabmodalprompt-button1").click(); - await TestUtils.waitForCondition( - () => promptElement.parentNode == null, - "tabprompt should be removed" - ); - } + let promptOpenedPromise = BrowserTestUtils.promiseAlertDialogOpen("cancel"); + EventUtils.synthesizeKey("VK_RETURN"); + await promptOpenedPromise; + await TestUtils.waitForTick(); // Can't just compare directly with TEST_URL because the URL may be trimmed. // Just need it to not be the example.org thing we typed in. diff --git a/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js b/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js index 62b0ed4f2b..52596095c4 100644 --- a/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js +++ b/browser/base/content/test/tabPrompts/browser_confirmFolderUpload.js @@ -88,7 +88,7 @@ async function testUploadPrompt(confirmUpload) { await ContentTask.spawn(browser, { path }, args => { let MockFilePicker = content.SpecialPowers.MockFilePicker; MockFilePicker.init( - content, + content.browsingContext, "A Mock File Picker", content.SpecialPowers.Ci.nsIFilePicker.modeGetFolder ); diff --git a/browser/base/content/test/tabPrompts/browser_contentOrigins.js b/browser/base/content/test/tabPrompts/browser_contentOrigins.js index 10c8809490..2bf4ba6039 100644 --- a/browser/base/content/test/tabPrompts/browser_contentOrigins.js +++ b/browser/base/content/test/tabPrompts/browser_contentOrigins.js @@ -129,11 +129,7 @@ async function checkDialog( add_setup(async function () { await SpecialPowers.pushPrefEnv({ - set: [ - ["prompts.contentPromptSubDialog", true], - ["prompts.modalType.httpAuth", Ci.nsIPrompt.MODAL_TYPE_TAB], - ["prompts.tabChromePromptSubDialog", true], - ], + set: [["prompts.modalType.httpAuth", Ci.nsIPrompt.MODAL_TYPE_TAB]], }); }); diff --git a/browser/base/content/test/tabPrompts/browser_multiplePrompts.js b/browser/base/content/test/tabPrompts/browser_multiplePrompts.js index 597b7dfd6f..65c8b3eff4 100644 --- a/browser/base/content/test/tabPrompts/browser_multiplePrompts.js +++ b/browser/base/content/test/tabPrompts/browser_multiplePrompts.js @@ -1,13 +1,7 @@ "use strict"; -const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( - "prompts.contentPromptSubDialog", - false -); - /** - * Goes through a stacked series of dialogs opened with - * CONTENT_PROMPT_SUBDIALOG set to true, and ensures that + * Goes through a stacked series of dialogs and ensures that * the oldest one is front-most and has the right type. It * then closes the oldest to newest dialog. * @@ -58,64 +52,6 @@ async function closeDialogs(tab, dialogCount) { is(dialogs.length, 0, "Dialogs should all be dismissed."); } -/** - * Goes through a stacked series of tabprompt modals opened with - * CONTENT_PROMPT_SUBDIALOG set to false, and ensures that - * the oldest one is front-most and has the right type. It also - * ensures that the other tabprompt modals are hidden. It - * then closes the oldest to newest dialog. - * - * @param {Element} tab The <tab> that has had tabprompt modals opened - * for it. - * @param {Number} promptCount How many modals we expected to have been - * opened. - * - * @return {Promise} - * @resolves {undefined} Once the modals have all been closed. - */ -async function closeTabModals(tab, promptCount) { - let promptElementsCount = promptCount; - while (promptElementsCount--) { - let promptElements = - tab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt"); - is( - promptElements.length, - promptElementsCount + 1, - "There should be " + (promptElementsCount + 1) + " prompt(s)." - ); - // The oldest should be the first. - let i = 0; - - for (let promptElement of promptElements) { - let prompt = tab.linkedBrowser.tabModalPromptBox.getPrompt(promptElement); - let expectedType = ["alert", "prompt", "confirm"][i % 3]; - is( - prompt.Dialog.args.text, - expectedType + " countdown #" + i, - "The #" + i + " alert should be labelled as such." - ); - if (i !== promptElementsCount) { - is(prompt.element.hidden, true, "This prompt should be hidden."); - i++; - continue; - } - - is(prompt.element.hidden, false, "The last prompt should not be hidden."); - prompt.onButtonClick(0); - - // The click is handled async; wait for an event loop turn for that to - // happen. - await new Promise(function (resolve) { - Services.tm.dispatchToMainThread(resolve); - }); - } - } - - let promptElements = - tab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt"); - is(promptElements.length, 0, "Prompts should all be dismissed."); -} - /* * This test triggers multiple alerts on one single tab, because it"s possible * for web content to do so. The behavior is described in bug 1266353. @@ -161,11 +97,7 @@ add_task(async function () { await promptsOpenedPromise; - if (CONTENT_PROMPT_SUBDIALOG) { - await closeDialogs(tab, PROMPTCOUNT); - } else { - await closeTabModals(tab, PROMPTCOUNT); - } + await closeDialogs(tab, PROMPTCOUNT); BrowserTestUtils.removeTab(tab); }); diff --git a/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js b/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js index 6b116b71f9..2fa3752881 100644 --- a/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js +++ b/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js @@ -22,118 +22,7 @@ registerCleanupFunction(function () { * the user to enable this automatically re-selecting. We then check that * checking the checkbox does actually enable that behaviour. */ -add_task(async function test_old_modal_ui() { - // We're intentionally testing the old modal mechanism, so disable the new one. - await SpecialPowers.pushPrefEnv({ - set: [["prompts.contentPromptSubDialog", false]], - }); - - let firstTab = gBrowser.selectedTab; - // load page that opens prompt when page is hidden - let openedTab = await BrowserTestUtils.openNewForegroundTab( - gBrowser, - pageWithAlert, - true - ); - let openedTabGotAttentionPromise = BrowserTestUtils.waitForAttribute( - "attention", - openedTab - ); - // switch away from that tab again - this triggers the alert. - await BrowserTestUtils.switchTab(gBrowser, firstTab); - // ... but that's async on e10s... - await openedTabGotAttentionPromise; - // check for attention attribute - is( - openedTab.hasAttribute("attention"), - true, - "Tab with alert should have 'attention' attribute." - ); - ok(!openedTab.selected, "Tab with alert should not be selected"); - - // switch tab back, and check the checkbox is displayed: - await BrowserTestUtils.switchTab(gBrowser, openedTab); - // check the prompt is there, and the extra row is present - let promptElements = - openedTab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt"); - is(promptElements.length, 1, "There should be 1 prompt"); - let ourPromptElement = promptElements[0]; - let checkbox = ourPromptElement.querySelector( - "checkbox[label*='example.com']" - ); - ok(checkbox, "The checkbox should be there"); - ok(!checkbox.checked, "Checkbox shouldn't be checked"); - // tick box and accept dialog - checkbox.checked = true; - let ourPrompt = - openedTab.linkedBrowser.tabModalPromptBox.getPrompt(ourPromptElement); - ourPrompt.onButtonClick(0); - // Wait for that click to actually be handled completely. - await new Promise(function (resolve) { - Services.tm.dispatchToMainThread(resolve); - }); - // check permission is set - is( - Services.perms.ALLOW_ACTION, - PermissionTestUtils.testPermission(pageWithAlert, "focus-tab-by-prompt"), - "Tab switching should now be allowed" - ); - - // Check if the control center shows the correct permission. - let shown = BrowserTestUtils.waitForEvent( - window, - "popupshown", - true, - event => event.target == gPermissionPanel._permissionPopup - ); - gPermissionPanel._identityPermissionBox.click(); - await shown; - let labelText = SitePermissions.getPermissionLabel("focus-tab-by-prompt"); - let permissionsList = document.getElementById( - "permission-popup-permission-list" - ); - let label = permissionsList.querySelector( - ".permission-popup-permission-label" - ); - is(label.textContent, labelText); - gPermissionPanel._permissionPopup.hidePopup(); - - // Check if the identity icon signals granted permission. - ok( - gPermissionPanel._identityPermissionBox.hasAttribute("hasPermissions"), - "identity-box signals granted permissions" - ); - - let openedTabSelectedPromise = BrowserTestUtils.waitForAttribute( - "selected", - openedTab, - "true" - ); - // switch to other tab again - await BrowserTestUtils.switchTab(gBrowser, firstTab); - - // This is sync in non-e10s, but in e10s we need to wait for this, so yield anyway. - // Note that the switchTab promise doesn't actually guarantee anything about *which* - // tab ends up as selected when its event fires, so using that here wouldn't work. - await openedTabSelectedPromise; - // should be switched back - ok(openedTab.selected, "Ta-dah, the other tab should now be selected again!"); - - // In e10s, with the conformant promise scheduling, we have to wait for next tick - // to ensure that the prompt is open before removing the opened tab, because the - // promise callback of 'openedTabSelectedPromise' could be done at the middle of - // RemotePrompt.openTabPrompt() while 'DOMModalDialogClosed' event is fired. - await TestUtils.waitForTick(); - - BrowserTestUtils.removeTab(openedTab); -}); - -add_task(async function test_new_modal_ui() { - // We're intentionally testing the new modal mechanism, so make sure it's enabled. - await SpecialPowers.pushPrefEnv({ - set: [["prompts.contentPromptSubDialog", true]], - }); - +add_task(async function test_modal_ui() { // Make sure we clear the focus tab permission set in the previous test PermissionTestUtils.remove(pageWithAlert, "focus-tab-by-prompt"); diff --git a/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js b/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js index 50b94e1a36..5cac19f6af 100644 --- a/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js +++ b/browser/base/content/test/tabdialogs/browser_tabdialogbox_content_prompts.js @@ -3,7 +3,6 @@ "use strict"; -const CONTENT_PROMPT_PREF = "prompts.contentPromptSubDialog"; const TEST_ROOT_CHROME = getRootDirectory(gTestPath); const TEST_DIALOG_PATH = TEST_ROOT_CHROME + "subdialog.xhtml"; @@ -41,13 +40,6 @@ var commonDialogsBundle = Services.strings.createBundle( "chrome://global/locale/commonDialogs.properties" ); -// Setup. -add_setup(async function () { - await SpecialPowers.pushPrefEnv({ - set: [[CONTENT_PROMPT_PREF, true]], - }); -}); - /** * Test that a manager for content prompts is added to tab dialog box. */ diff --git a/browser/base/content/test/tabs/browser.toml b/browser/base/content/test/tabs/browser.toml index 8008d70f0c..1b4a6900bf 100644 --- a/browser/base/content/test/tabs/browser.toml +++ b/browser/base/content/test/tabs/browser.toml @@ -236,7 +236,6 @@ support-files = [ ] ["browser_overflowScroll.js"] -fail-if = ["a11y_checks"] # Bugs 1854233 and 1873049 scrollbutton-down/up are not labeled skip-if = [ "win11_2009", # Bug 1797751 ] diff --git a/browser/base/content/test/tabs/browser_close_during_beforeunload.js b/browser/base/content/test/tabs/browser_close_during_beforeunload.js index 32bbb65b62..035884d713 100644 --- a/browser/base/content/test/tabs/browser_close_during_beforeunload.js +++ b/browser/base/content/test/tabs/browser_close_during_beforeunload.js @@ -4,14 +4,7 @@ // beforeunload confirmation ignores the beforeunload listener and // unblocks the original close call. -const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( - "prompts.contentPromptSubDialog", - false -); - -const DIALOG_TOPIC = CONTENT_PROMPT_SUBDIALOG - ? "common-dialog-loaded" - : "tabmodal-dialog-loaded"; +const DIALOG_TOPIC = "common-dialog-loaded"; add_task(async function () { await SpecialPowers.pushPrefEnv({ diff --git a/browser/base/content/test/tabs/browser_multiselect_tabs_bookmark.js b/browser/base/content/test/tabs/browser_multiselect_tabs_bookmark.js index a24e72c0bb..cd3728edcd 100644 --- a/browser/base/content/test/tabs/browser_multiselect_tabs_bookmark.js +++ b/browser/base/content/test/tabs/browser_multiselect_tabs_bookmark.js @@ -48,11 +48,6 @@ add_task(async function test() { false, "Bookmark Selected Tabs is hidden" ); - is( - PlacesCommandHook.uniqueSelectedPages.length, - 1, - "No more than one unique selected page" - ); info("Add a different page to selection"); let tab4 = await addTab_example_com(); @@ -69,11 +64,6 @@ add_task(async function test() { false, "Bookmark Selected Tabs is hidden" ); - is( - PlacesCommandHook.uniqueSelectedPages.length, - 2, - "More than one unique selected page" - ); for (let tab of [tab1, tab2, tab3, tab4]) { BrowserTestUtils.removeTab(tab); diff --git a/browser/base/content/test/tabs/browser_tab_preview.js b/browser/base/content/test/tabs/browser_tab_preview.js index e3dd1b6842..718afbb940 100644 --- a/browser/base/content/test/tabs/browser_tab_preview.js +++ b/browser/base/content/test/tabs/browser_tab_preview.js @@ -34,7 +34,7 @@ add_setup(async function () { set: [ ["browser.tabs.cardPreview.enabled", true], ["browser.tabs.cardPreview.showThumbnails", false], - ["browser.tabs.cardPreview.delayMs", 0], + ["ui.tooltip.delay_ms", 0], ], }); }); @@ -46,7 +46,7 @@ add_setup(async function () { * 2. Tab preview card shows the correct preview for the tab being hovered * 3. Tab preview card is dismissed when the mouse leaves the tab bar */ -add_task(async () => { +add_task(async function hoverTests() { const tabUrl1 = "data:text/html,<html><head><title>First New Tab</title></head><body>Hello</body></html>"; const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, tabUrl1); @@ -85,6 +85,11 @@ add_task(async () => { BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab2); + + // Move the mouse outside of the tab strip. + EventUtils.synthesizeMouseAtCenter(document.documentElement, { + type: "mouseover", + }); }); /** @@ -92,7 +97,7 @@ add_task(async () => { * when browser.tabs.cardPreview.showThumbnails is set to true, * while the currently selected tab never displays a thumbnail in its preview. */ -add_task(async () => { +add_task(async function thumbnailTests() { await SpecialPowers.pushPrefEnv({ set: [["browser.tabs.cardPreview.showThumbnails", true]], }); @@ -120,15 +125,28 @@ add_task(async () => { "Tab2 (selected) does not contain thumbnail" ); + const previewHidden = BrowserTestUtils.waitForEvent( + document.getElementById("tabbrowser-tab-preview"), + "previewhidden" + ); + BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab2); await SpecialPowers.popPrefEnv(); + + // Removing the tab should close the preview. + await previewHidden; + + // Move the mouse outside of the tab strip. + EventUtils.synthesizeMouseAtCenter(document.documentElement, { + type: "mouseover", + }); }); /** * Wheel events at the document-level of the window should hide the preview. */ -add_task(async () => { +add_task(async function wheelTests() { const tabUrl1 = "about:blank"; const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, tabUrl1); const tabUrl2 = "about:blank"; @@ -141,14 +159,49 @@ add_task(async () => { document.getElementById("tabbrowser-tab-preview"), "previewhidden" ); - EventUtils.synthesizeMouse(tabs, 0, tabs.outerHeight + 1, { - wheel: true, - deltaY: -1, - deltaMode: WheelEvent.DOM_DELTA_LINE, - }); + + // Copied from apz_test_native_event_utils.js + let message = 0; + switch (AppConstants.platform) { + case "win": + message = 0x020a; + break; + case "linux": + message = 4; + break; + case "macosx": + message = 1; + break; + } + + let rect = tabs.getBoundingClientRect(); + let screenRect = window.windowUtils.toScreenRect( + rect.x, + rect.y, + rect.width, + rect.height + ); + window.windowUtils.sendNativeMouseScrollEvent( + screenRect.left, + screenRect.bottom, + message, + 0, + 3, + 0, + 0, + Ci.nsIDOMWindowUtils.MOUSESCROLL_SCROLL_LINES, + tabs, + null + ); + await previewHidden; BrowserTestUtils.removeTab(tab1); BrowserTestUtils.removeTab(tab2); await SpecialPowers.popPrefEnv(); + + // Move the mouse outside of the tab strip. + EventUtils.synthesizeMouseAtCenter(document.documentElement, { + type: "mouseover", + }); }); diff --git a/browser/base/content/test/webextensions/browser_permissions_local_file.js b/browser/base/content/test/webextensions/browser_permissions_local_file.js index a2fdc34db3..7f8f256e14 100644 --- a/browser/base/content/test/webextensions/browser_permissions_local_file.js +++ b/browser/base/content/test/webextensions/browser_permissions_local_file.js @@ -10,7 +10,7 @@ async function installFile(filename) { file.leafName = filename; let MockFilePicker = SpecialPowers.MockFilePicker; - MockFilePicker.init(window); + MockFilePicker.init(window.browsingContext); MockFilePicker.setFiles([file]); MockFilePicker.afterOpenCallback = MockFilePicker.cleanup; diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index ae9c1d0a9a..5967c878b3 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -431,8 +431,7 @@ async function openPreferences(paneID, extraArgs) { } } } - let preferencesURL = - "about:preferences" + + let preferencesURLSuffix = (params ? "?" + params : "") + (friendlyCategoryName ? "#" + friendlyCategoryName : ""); let newLoad = true; @@ -444,7 +443,7 @@ async function openPreferences(paneID, extraArgs) { let supportsStringPrefURL = Cc[ "@mozilla.org/supports-string;1" ].createInstance(Ci.nsISupportsString); - supportsStringPrefURL.data = preferencesURL; + supportsStringPrefURL.data = "about:preferences" + preferencesURLSuffix; windowArguments.appendElement(supportsStringPrefURL); win = Services.ww.openWindow( @@ -458,11 +457,28 @@ async function openPreferences(paneID, extraArgs) { let shouldReplaceFragment = friendlyCategoryName ? "whenComparingAndReplace" : "whenComparing"; - newLoad = !win.switchToTabHavingURI(preferencesURL, true, { - ignoreFragment: shouldReplaceFragment, - replaceQueryString: true, - triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), - }); + newLoad = !win.switchToTabHavingURI( + "about:settings" + preferencesURLSuffix, + false, + { + ignoreFragment: shouldReplaceFragment, + replaceQueryString: true, + triggeringPrincipal: + Services.scriptSecurityManager.getSystemPrincipal(), + } + ); + if (newLoad) { + newLoad = !win.switchToTabHavingURI( + "about:preferences" + preferencesURLSuffix, + true, + { + ignoreFragment: shouldReplaceFragment, + replaceQueryString: true, + triggeringPrincipal: + Services.scriptSecurityManager.getSystemPrincipal(), + } + ); + } browser = win.gBrowser.selectedBrowser; } @@ -533,11 +549,6 @@ function buildHelpMenu() { if (typeof gSafeBrowsing != "undefined") { gSafeBrowsing.setReportPhishingMenu(); } - - if (NimbusFeatures.deviceMigration.getVariable("helpMenuHidden")) { - let helpMenuItem = document.getElementById("helpSwitchDevice"); - helpMenuItem.hidden = true; - } } function isElementVisible(aElement) { diff --git a/browser/base/content/webext-panels.xhtml b/browser/base/content/webext-panels.xhtml index 902fa7e7b3..f421d9bf80 100644 --- a/browser/base/content/webext-panels.xhtml +++ b/browser/base/content/webext-panels.xhtml @@ -28,6 +28,7 @@ <html:link rel="localization" href="toolkit/branding/brandings.ftl"/> <html:link rel="localization" href="toolkit/global/textActions.ftl"/> <html:link rel="localization" href="browser/browserContext.ftl"/> + <html:link rel="localization" href="preview/select-translations.ftl"/> </linkset> <commandset id="mainCommandset"> diff --git a/browser/base/jar.mn b/browser/base/jar.mn index e3eb0e6e5f..1342630d54 100644 --- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -52,6 +52,7 @@ browser.jar: content/browser/browser-pageActions.js (content/browser-pageActions.js) content/browser/browser-pagestyle.js (content/browser-pagestyle.js) content/browser/browser-places.js (content/browser-places.js) + content/browser/browser-profiles.js (content/browser-profiles.js) content/browser/browser-safebrowsing.js (content/browser-safebrowsing.js) content/browser/browser-sidebar.js (content/browser-sidebar.js) content/browser/browser-siteIdentity.js (content/browser-siteIdentity.js) diff --git a/browser/base/moz.build b/browser/base/moz.build index 98c3992a5d..9813d68058 100644 --- a/browser/base/moz.build +++ b/browser/base/moz.build @@ -85,8 +85,4 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk", "cocoa"): if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk"): DEFINES["MENUBAR_CAN_AUTOHIDE"] = 1 -if CONFIG["OS_ARCH"] == "WINNT" and CONFIG["MOZ_DEFAULT_BROWSER_AGENT"]: - # Impacts `/toolkit/content/license.html`. - DEFINES["MOZ_DEFAULT_BROWSER_AGENT"] = True - JAR_MANIFESTS += ["jar.mn"] diff --git a/browser/base/triage-tabbrowser.json b/browser/base/triage-tabbrowser.json new file mode 100644 index 0000000000..237be342e3 --- /dev/null +++ b/browser/base/triage-tabbrowser.json @@ -0,0 +1,51 @@ +{ + "triagers": { + "Dão Gottwald": { + "bzmail": "dao+bmo@mozilla.com" + }, + "DJ Walker": { + "bzmail": "dwalker@mozilla.com" + }, + "Marco Bonardo": { + "bzmail": "mak@mozilla.com" + }, + "Mike Conley": { + "bzmail": "mconley@mozilla.com" + }, + "Niklas Baumgardner": { + "bzmail": "nbaumgardner@mozilla.com" + } + }, + "duty-start-dates": { + "2024-03-01": "Marco Bonardo", + "2024-03-16": "Dão Gottwald", + "2024-04-01": "Mike Conley", + "2024-04-16": "Dão Gottwald", + "2024-05-01": "Marco Bonardo", + "2024-05-16": "Mike Conley", + "2024-06-01": "Niklas Baumgardner", + "2024-06-16": "Dão Gottwald", + "2024-07-01": "DJ Walker", + "2024-07-16": "Marco Bonardo", + "2024-08-01": "Mike Conley", + "2024-08-16": "Niklas Baumgardner", + "2024-09-01": "Dão Gottwald", + "2024-09-16": "DJ Walker", + "2024-10-01": "Marco Bonardo", + "2024-10-16": "Mike Conley", + "2024-11-01": "Niklas Baumgardner", + "2024-11-16": "Dão Gottwald", + "2024-12-01": "DJ Walker", + "2024-12-16": "Marco Bonardo", + "2025-01-01": "Mike Conley", + "2025-01-16": "Niklas Baumgardner", + "2025-02-01": "Dão Gottwald", + "2025-02-16": "DJ Walker", + "2025-03-01": "Marco Bonardo", + "2025-03-16": "Mike Conley", + "2025-04-01": "Niklas Baumgardner", + "2025-04-16": "Dão Gottwald", + "2025-05-01": "DJ Walker", + "2025-05-16": "Marco Bonardo" + } +} |