summaryrefslogtreecommitdiffstats
path: root/browser/base/content
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content')
-rw-r--r--browser/base/content/appmenu-viewcache.inc.xhtml135
-rw-r--r--browser/base/content/browser-addons.js110
-rw-r--r--browser/base/content/browser-allTabsMenu.inc.xhtml4
-rw-r--r--browser/base/content/browser-allTabsMenu.js15
-rw-r--r--browser/base/content/browser-box.inc.xhtml4
-rw-r--r--browser/base/content/browser-commands.js17
-rw-r--r--browser/base/content/browser-init.js1107
-rw-r--r--browser/base/content/browser-menubar.inc19
-rw-r--r--browser/base/content/browser-pageActions.js2
-rw-r--r--browser/base/content/browser-places.js15
-rw-r--r--browser/base/content/browser-sets.inc8
-rw-r--r--browser/base/content/browser-siteIdentity.js1
-rw-r--r--browser/base/content/browser-sitePermissionPanel.js3
-rw-r--r--browser/base/content/browser-siteProtections.js9
-rw-r--r--browser/base/content/browser-sync.js326
-rw-r--r--browser/base/content/browser.js1261
-rw-r--r--browser/base/content/browser.js.globals7
-rw-r--r--browser/base/content/browser.xhtml3
-rw-r--r--browser/base/content/macWindow.inc.xhtml1
-rw-r--r--browser/base/content/main-popupset.inc.xhtml33
-rw-r--r--browser/base/content/navigator-toolbox.inc.xhtml9
-rw-r--r--browser/base/content/nsContextMenu.js39
-rw-r--r--browser/base/content/popup-notifications.inc2
-rw-r--r--browser/base/content/spotlight.html1
-rw-r--r--browser/base/content/tabbrowser-tabs.js15
-rw-r--r--browser/base/content/tabbrowser.css101
-rw-r--r--browser/base/content/tabbrowser.js314
-rw-r--r--browser/base/content/test/about/browser.toml1
-rw-r--r--browser/base/content/test/about/browser_aboutNetError_csp_iframe.js4
-rw-r--r--browser/base/content/test/about/browser_aboutNetError_xfo_iframe.js4
-rw-r--r--browser/base/content/test/alerts/browser.toml4
-rw-r--r--browser/base/content/test/alerts/browser_notification_close.js25
-rw-r--r--browser/base/content/test/alerts/browser_notification_replace.js66
-rw-r--r--browser/base/content/test/contextMenu/browser.toml2
-rw-r--r--browser/base/content/test/contextMenu/browser_contextmenu.js69
-rw-r--r--browser/base/content/test/contextMenu/browser_contextmenu_cross_boundary_selection.js73
-rw-r--r--browser/base/content/test/contextMenu/browser_strip_on_share_link.js12
-rw-r--r--browser/base/content/test/general/browser_documentnavigation.js4
-rw-r--r--browser/base/content/test/performance/browser_preferences_usage.js19
-rw-r--r--browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js414
-rw-r--r--browser/base/content/test/sidebar/browser_sidebar_adopt.js8
-rw-r--r--browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js6
-rw-r--r--browser/base/content/test/sidebar/browser_sidebar_keys.js16
-rw-r--r--browser/base/content/test/sidebar/browser_sidebar_move.js18
-rw-r--r--browser/base/content/test/sidebar/browser_sidebar_persist.js4
-rw-r--r--browser/base/content/test/sidebar/browser_sidebar_switcher.js43
-rw-r--r--browser/base/content/test/static/browser_all_files_referenced.js16
-rw-r--r--browser/base/content/test/static/browser_parsable_css.js6
-rw-r--r--browser/base/content/test/static/browser_parsable_script.js1
-rw-r--r--browser/base/content/test/static/browser_sentence_case_strings.js2
-rw-r--r--browser/base/content/test/sync/browser_contextmenu_sendpage.js57
-rw-r--r--browser/base/content/test/sync/browser_sync.js10
-rw-r--r--browser/base/content/test/tabPrompts/browser.toml2
-rw-r--r--browser/base/content/test/tabPrompts/browser_promptDelays.js113
-rw-r--r--browser/base/content/test/tabPrompts/browser_promptFocus.js3
-rw-r--r--browser/base/content/test/tabs/browser.toml4
-rw-r--r--browser/base/content/test/tabs/browser_blank_tab_label.js49
-rw-r--r--browser/base/content/test/tabs/browser_multiselect_tabs_close_duplicate_tabs.js178
-rw-r--r--browser/base/content/test/tabs/browser_tab_preview.js94
-rw-r--r--browser/base/content/test/tabs/browser_visibleTabs_contextMenu.js19
-rw-r--r--browser/base/content/test/webextensions/browser_aboutaddons_blanktab.js2
-rw-r--r--browser/base/content/test/webextensions/browser_extension_sideloading.js10
-rw-r--r--browser/base/content/test/webextensions/browser_permissions_local_file.js4
-rw-r--r--browser/base/content/test/webextensions/browser_update_interactive_noprompt.js2
-rw-r--r--browser/base/content/test/webextensions/head.js2
-rw-r--r--browser/base/content/utilityOverlay.js5
66 files changed, 2831 insertions, 2101 deletions
diff --git a/browser/base/content/appmenu-viewcache.inc.xhtml b/browser/base/content/appmenu-viewcache.inc.xhtml
index 6d838bd9d6..9633c7d79d 100644
--- a/browser/base/content/appmenu-viewcache.inc.xhtml
+++ b/browser/base/content/appmenu-viewcache.inc.xhtml
@@ -3,14 +3,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html:template id="appMenu-viewCache">
- <!-- This is a placeholder app menu which should be replaced with the "real"
- Proton app menu before the Proton pref starts getting enabled. -->
- <panelview id="appMenu-protonMainView" class="PanelUI-subView"
+ <panelview id="appMenu-mainView" class="PanelUI-subView"
lockpanelvertical="true">
<vbox class="panel-subview-body">
- <vbox id="appMenu-proton-addon-banners"/>
- <toolbarbutton id="appMenu-proton-update-banner" class="panel-banner-item subviewbutton"
- oncommand="PanelUI._onBannerItemSelected(event)"
+ <vbox id="appMenu-addon-banners"/>
+ <toolbarbutton id="appMenu-update-banner" class="panel-banner-item subviewbutton"
wrap="true"
hidden="true"/>
<toolbaritem id="appMenu-fxa-status2"
@@ -19,7 +16,7 @@
<html:div id="appMenu-fxa-text" data-l10n-id="appmenu-fxa-sync-and-save-data2"/>
<toolbarbutton id="appMenu-fxa-label2"
class="subviewbutton"
- oncommand="gSync.toggleAccountPanel(this, event)">
+ >
<vbox flex="1">
<label id="appMenu-header-title"
crop="end"/>
@@ -33,7 +30,6 @@
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"
@@ -56,12 +52,12 @@
class="subviewbutton subviewbutton-nav"
data-l10n-id="library-bookmarks-menu"
closemenu="none"
- oncommand="BookmarkingUI.showSubView(this);"/>
+ />
<toolbarbutton id="appMenu-history-button"
class="subviewbutton subviewbutton-nav"
data-l10n-id="appmenuitem-history"
closemenu="none"
- oncommand="PanelUI.showSubView('PanelUI-history', this)"/>
+ />
<toolbarbutton id="appMenu-downloads-button"
class="subviewbutton"
data-l10n-id="appmenuitem-downloads"
@@ -70,7 +66,6 @@
<toolbarbutton id="appMenu-passwords-button"
class="subviewbutton"
data-l10n-id="appmenuitem-passwords"
- oncommand="LoginHelper.openPasswordManager(window, { entryPoint: 'mainmenu' })"
/>
<toolbarbutton id="appMenu-extensions-themes-button"
class="subviewbutton"
@@ -119,13 +114,6 @@
class="subviewbutton subviewbutton-iconic"
data-l10n-id="appmenuitem-fullscreen"
type="checkbox"
-# Note that we're custom-handling this click to make sure the panel disappears
-# before entering fullscreen, as it does some odd moving about on the screen
-# in the middle of the fullscreen transition otherwise.
- oncommand="
- this.closest('panel').hidePopup();
- setTimeout(() => BrowserCommands.fullScreen(), 0);
- "
tooltip="dynamic-shortcut-tooltip">
<observes element="View:FullScreen" attribute="checked"/>
</toolbarbutton>
@@ -137,12 +125,12 @@
#ifdef XP_MACOSX
key="key_preferencesCmdMac"
#endif
- oncommand="openPreferences()"/>
+ />
<toolbarbutton id="appMenu-more-button2"
class="subviewbutton subviewbutton-nav"
data-l10n-id="appmenuitem-more-tools"
closemenu="none"
- oncommand="PanelUI.showMoreToolsPanel(this);"/>
+ />
<toolbarbutton id="appMenu-report-broken-site-button"
class="subviewbutton subviewbutton-nav"
data-l10n-id="appmenuitem-report-broken-site"
@@ -153,7 +141,7 @@
class="subviewbutton subviewbutton-nav"
data-l10n-id="appmenuitem-help"
closemenu="none"
- oncommand="PanelUI.showSubView('PanelUI-helpView', this)"/>
+ />
#ifndef XP_MACOSX
<toolbarseparator/>
<toolbarbutton id="appMenu-quit-button2"
@@ -171,16 +159,16 @@
data-l10n-id="appmenu-recently-closed-tabs"
class="subviewbutton subviewbutton-nav"
closemenu="none"
- oncommand="PanelUI.showSubView('appMenu-library-recentlyClosedTabs', this)"/>
+ />
<toolbarbutton id="appMenuRecentlyClosedWindows"
data-l10n-id="appmenu-recently-closed-windows"
class="subviewbutton subviewbutton-nav"
closemenu="none"
- oncommand="PanelUI.showSubView('appMenu-library-recentlyClosedWindows', this)"/>
+ />
<toolbarbutton id="appMenuSearchHistory"
data-l10n-id="appmenu-search-history"
class="subviewbutton"
- oncommand="PlacesCommandHook.searchHistory()"/>
+ />
<toolbarbutton id="appMenu-restoreSession"
data-l10n-id="appmenu-restore-session"
class="subviewbutton"
@@ -206,7 +194,7 @@
<toolbarbutton id="PanelUI-historyMore"
class="subviewbutton panel-subview-footer-button"
data-l10n-id="appmenu-manage-history"
- oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
+ />
</panelview>
<panelview id="PanelUI-profiles" flex="1">
@@ -217,10 +205,10 @@
<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/>
@@ -230,15 +218,15 @@
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>
@@ -262,12 +250,12 @@
<toolbarbutton id="panelMenu_searchBookmarks"
data-l10n-id="bookmarks-search"
class="subviewbutton"
- oncommand="PlacesCommandHook.searchBookmarks();"/>
+ />
<toolbarbutton id="panelMenu_viewBookmarksToolbar"
class="subviewbutton"
data-l10n-id="bookmarks-tools-toolbar-visibility-panel"
data-l10n-args='{ "isVisible": false }'
- oncommand="BookmarkingUI.toggleBookmarksToolbar('bookmark-tools');"/>
+ />
<toolbarseparator/>
<html:h2 id="panelMenu_recentBookmarks"
data-l10n-id="bookmarks-recent-bookmarks-panel-subheader"
@@ -460,8 +448,6 @@
<toolbarbutton id="PanelUI-remotetabs-syncnow"
align="center"
class="subviewbutton"
- oncommand="gSync.doSync();"
- onmouseover="gSync.refreshSyncButtonsTooltip();"
closemenu="none">
<hbox flex="1">
<image class="syncNowBtn"/>
@@ -476,7 +462,7 @@
<toolbarbutton id="PanelUI-remotetabs-view-managedevices"
class="subviewbutton"
data-l10n-id="appmenuitem-fxa-manage-account"
- oncommand="gSync.openDevicesManagementPage('syncedtabs-menupanel');">
+ >
<observes element="sidebar-box" attribute="positionend"/>
</toolbarbutton>
<toolbarseparator id="PanelUI-remotetabs-separator"/>
@@ -503,7 +489,7 @@
<toolbarbutton class="PanelUI-remotetabs-button"
id="PanelUI-remotetabs-tabsdisabledpane-button"
data-l10n-id="appmenu-remote-tabs-opensettings"
- oncommand="gSync.openPrefs('synced-tabs');"/>
+ />
</hbox>
</vbox>
</hbox>
@@ -517,7 +503,7 @@
<toolbarbutton id="PanelUI-remotetabs-connect-device-button"
class="PanelUI-remotetabs-button"
data-l10n-id="appmenu-remote-tabs-connectdevice"
- oncommand="gSync.openConnectAnotherDevice('synced-tabs');"/>
+ />
</vbox>
</hbox>
</deck>
@@ -535,7 +521,7 @@
<toolbarbutton class="PanelUI-remotetabs-button"
id="PanelUI-remotetabs-setupsync-button"
data-l10n-id="appmenu-remote-tabs-sign-into-sync"
- oncommand="gSync.openPrefs('synced-tabs');"/>
+ />
</vbox>
<!-- When Sync is not enabled -->
<vbox id="PanelUI-remotetabs-syncdisabled"
@@ -548,7 +534,7 @@
<toolbarbutton class="PanelUI-remotetabs-button"
id="PanelUI-remotetabs-syncdisabled-button"
data-l10n-id="appmenu-remote-tabs-turn-on-sync"
- oncommand="gSync.openPrefs('synced-tabs');"/>
+ />
</vbox>
<!-- When Sync needs re-authentication -->
<vbox id="PanelUI-remotetabs-reauthsync"
@@ -561,7 +547,7 @@
<toolbarbutton class="PanelUI-remotetabs-button"
id="PanelUI-remotetabs-reauthsync-button"
data-l10n-id="appmenu-remote-tabs-sign-into-sync"
- oncommand="gSync.openPrefs('synced-tabs');"/>
+ />
</vbox>
<!-- When Sync needs verification -->
<vbox id="PanelUI-remotetabs-unverified"
@@ -574,7 +560,7 @@
<toolbarbutton class="PanelUI-remotetabs-button"
id="PanelUI-remotetabs-unverified-button"
data-l10n-id="appmenu-remote-tabs-opensettings"
- oncommand="gSync.openPrefs('synced-tabs');"/>
+ />
</vbox>
</hbox>
</vbox>
@@ -585,7 +571,7 @@
<toolbarbutton id="fxa-manage-account-button"
align="center"
class="subviewbutton"
- oncommand="gSync.clickFxAMenuHeaderButton(this);">
+ >
<vbox flex="1">
<label id="fxa-menu-header-title"
crop="end"
@@ -599,8 +585,6 @@
<toolbarbutton id="PanelUI-fxa-menu-syncnow-button"
align="center"
class="subviewbutton"
- oncommand="gSync.doSyncFromFxaMenu(this);"
- onmouseover="gSync.refreshSyncButtonsTooltip();"
closemenu="none">
<hbox flex="1">
<image id="PanelUI-appMenu-fxa-image-last-synced"
@@ -617,43 +601,34 @@
<toolbarbutton id="PanelUI-fxa-menu-setup-sync-button"
class="subviewbutton"
data-l10n-id="appmenu-fxa-setup-sync"
- oncommand="gSync.openPrefsFromFxaMenu('sync_settings', this);"/>
+ />
<!-- The `Connect Another Device` button is disabled by default until the user logs into Sync. -->
<toolbarbutton id="PanelUI-fxa-menu-connect-device-button"
class="subviewbutton"
data-l10n-id="fxa-menu-connect-another-device"
disabled="true"
- oncommand="gSync.openConnectAnotherDeviceFromFxaMenu(this);"/>
+ />
<toolbarbutton id="PanelUI-fxa-menu-sendtab-button"
class="subviewbutton subviewbutton-nav"
data-l10n-id="fxa-menu-send-tab-to-device"
data-l10n-args='{"tabCount":1}'
closemenu="none"
- oncommand="gSync.showSendToDeviceViewFromFxaMenu(this);"/>
+ />
<toolbarbutton id="PanelUI-fxa-menu-sync-prefs-button"
class="subviewbutton"
data-l10n-id="fxa-menu-sync-settings"
hidden="true"
- oncommand="gSync.openPrefsFromFxaMenu('sync_settings', this);"/>
+ />
<toolbarseparator id="PanelUI-sign-out-separator" />
<toolbarbutton id="PanelUI-fxa-menu-account-signout-button"
class="subviewbutton"
data-l10n-id="fxa-menu-sign-out"
- oncommand="gSync.disconnect();"
hidden="true"/>
</vbox>
<!-- updateCTAPanel will control if we show this panel -->
- <vbox id="PanelUI-fxa-cta-menu">
- <toolbarbutton id="PanelUI-fxa-menu-sync-button" class="subviewbutton subviewbutton-iconic"
- oncommand="gSync.openPrefsFromFxaButton('sync_cta', this);">
- <vbox flex="1">
- <label id="fxa-menu-header-title" crop="end" data-l10n-id="fxa-menu-sync-title" />
- <label id="cta-menu-header-description" crop="end" data-l10n-id="fxa-menu-sync-description" />
- </vbox>
- </toolbarbutton>
+ <vbox id="PanelUI-fxa-cta-menu" hidden="true">
<toolbarseparator id="PanelUI-products-separator" />
- <toolbarbutton id="PanelUI-fxa-menu-monitor-button" class="subviewbutton subviewbutton-iconic"
- oncommand="gSync.openMonitorLink(this)">
+ <toolbarbutton id="PanelUI-fxa-menu-monitor-button" class="fxa-cta-button subviewbutton subviewbutton-iconic">
<vbox flex="1">
<hbox align="center">
<image class="PanelUI-fxa-menu-monitor-button ctaMenuLogo" role="presentation" />
@@ -662,8 +637,7 @@
<label id="cta-menu-header-description" crop="end" data-l10n-id="appmenuitem-monitor-description" />
</vbox>
</toolbarbutton>
- <toolbarbutton id="PanelUI-fxa-menu-relay-button" class="subviewbutton subviewbutton-iconic"
- oncommand="gSync.openRelayLink(this)">
+ <toolbarbutton id="PanelUI-fxa-menu-relay-button" class="fxa-cta-button subviewbutton subviewbutton-iconic">
<vbox flex="1">
<hbox align="center">
<image class="PanelUI-fxa-menu-relay-button ctaMenuLogo" role="presentation" />
@@ -672,8 +646,7 @@
<label id="cta-menu-header-description" crop="end" data-l10n-id="appmenuitem-relay-description" />
</vbox>
</toolbarbutton>
- <toolbarbutton id="PanelUI-fxa-menu-vpn-button" class="subviewbutton subviewbutton-iconic"
- oncommand="gSync.openVPNLink(this)">
+ <toolbarbutton id="PanelUI-fxa-menu-vpn-button" class="fxa-cta-button subviewbutton subviewbutton-iconic">
<vbox flex="1">
<hbox align="center">
<image class="PanelUI-fxa-menu-vpn-button ctaMenuLogo" role="presentation" />
@@ -715,7 +688,7 @@
<toolbarbutton id="PanelUI-fxa-menu-sendtab-not-configured-button"
class="PanelUI-fxa-signin-button"
data-l10n-id="appmenuitem-fxa-sign-in"
- oncommand="gSync.openPrefsFromFxaMenu('send_tab', this);"/>
+ />
</vbox>
</panelview>
@@ -726,7 +699,15 @@
<toolbarbutton id="PanelUI-fxa-menu-sendtab-connect-device-button"
class="PanelUI-fxa-signin-button"
data-l10n-id="appmenu-remote-tabs-connectdevice"
- oncommand="gSync.openConnectAnotherDeviceFromFxaMenu(this);"/>
+ />
+ </vbox>
+ </panelview>
+
+ <!-- This panelview holds the list of "inactive" tabs for devices -->
+ <panelview id="PanelUI-fxa-menu-inactive-tabs" class="PanelUI-subView PanelUI-remotetabs-clientcontainer">
+ <label itemtype="client">
+ </label>
+ <vbox class="panel-subview-body">
</vbox>
</panelview>
@@ -736,16 +717,16 @@
class="subviewbutton subviewbutton-nav"
data-l10n-id="library-bookmarks-menu"
closemenu="none"
- oncommand="BookmarkingUI.showSubView(this);"/>
+ />
<toolbarbutton id="appMenu-library-history-button"
class="subviewbutton subviewbutton-nav"
data-l10n-id="appmenuitem-history"
closemenu="none"
- oncommand="PanelUI.showSubView('PanelUI-history', this)"/>
+ />
<toolbarbutton id="appMenu-library-downloads-button"
class="subviewbutton"
data-l10n-id="appmenuitem-downloads"
- oncommand="DownloadsPanel.showDownloadsHistory();"/>
+ />
</vbox>
</panelview>
@@ -762,15 +743,29 @@
<button id="reset-pbm-panel-cancel-button"
class="footer-button"
data-l10n-id="reset-pbm-panel-cancel-button"
- oncommand="ResetPBMPanel.onCancel(this)"></button>
+ ></button>
<button slot="primary"
id="reset-pbm-panel-confirm-button"
class="footer-button"
data-l10n-id="reset-pbm-panel-confirm-button"
- oncommand="ResetPBMPanel.onConfirm(this)"></button>
+ ></button>
</html:moz-button-group>
</vbox>
</panelview>
+ <panelview id="content-analysis-panel" class="PanelUI-subView" role="document" mainview-with-header="true">
+ <vbox id="content-analysis-panel-container" role="alertdialog" aria-labelledby="content-analysis-header">
+ <hbox class="panel-header">
+ <html:h1 id="content-analysis-header" data-l10n-id="content-analysis-panel-title"/>
+ </hbox>
+ <description id="content-analysis-panel-description">
+ <html:a is="moz-support-link"
+ data-l10n-name="info"
+ class="learnMore"
+ support-page="data-loss-prevention"/>
+ </description>
+ </vbox>
+ </panelview>
+
#include ../../components/reportbrokensite/content/reportBrokenSitePanel.inc.xhtml
</html:template>
diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
index 1e97428e24..e00952b2dc 100644
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -618,7 +618,6 @@ var gXPInstallObserver = {
break;
}
case "addon-install-blocked": {
- await window.ensureCustomElements("moz-support-link");
// Dismiss the progress notification. Note that this is bad if
// there are multiple simultaneous installs happening, see
// bug 1329884 for a longer explanation.
@@ -1089,27 +1088,18 @@ var BrowserAddonUI = {
return { remove: result === 0, report: checkboxState.value };
},
- async reportAddon(addonId, reportEntryPoint) {
+ async reportAddon(addonId, _reportEntryPoint) {
let addon = addonId && (await AddonManager.getAddonByID(addonId));
if (!addon) {
return;
}
- // Do not open an additional about:addons tab if the abuse report should be
- // opened in its own tab.
- if (lazy.AbuseReporter.amoFormEnabled) {
- const amoUrl = lazy.AbuseReporter.getAMOFormURL({ addonId });
- window.openTrustedLinkIn(amoUrl, "tab", {
- // Make sure the newly open tab is going to be focused, independently
- // from general user prefs.
- forceForeground: true,
- });
- return;
- }
-
- const win = await BrowserOpenAddonsMgr("addons://list/extension");
-
- win.openAbuseReport({ addonId, reportEntryPoint });
+ const amoUrl = lazy.AbuseReporter.getAMOFormURL({ addonId });
+ window.openTrustedLinkIn(amoUrl, "tab", {
+ // Make sure the newly open tab is going to be focused, independently
+ // from general user prefs.
+ forceForeground: true,
+ });
},
async removeAddon(addonId) {
@@ -1137,7 +1127,85 @@ var BrowserAddonUI = {
return;
}
- BrowserOpenAddonsMgr("addons://detail/" + encodeURIComponent(addon.id));
+ this.openAddonsMgr("addons://detail/" + encodeURIComponent(addon.id));
+ },
+
+ /**
+ * Open about:addons page by given view id.
+ * @param {String} aView
+ * View id of page that will open.
+ * e.g. "addons://discover/"
+ * @param {Object} options
+ * {
+ * selectTabByViewId: If true, if there is the tab opening page having
+ * same view id, select the tab. Else if the current
+ * page is blank, load on it. Otherwise, open a new
+ * tab, then load on it.
+ * If false, if there is the tab opening
+ * about:addoons page, select the tab and load page
+ * for view id on it. Otherwise, leave the loading
+ * behavior to switchToTabHavingURI().
+ * If no options, handles as false.
+ * }
+ * @returns {Promise} When the Promise resolves, returns window object loaded the
+ * view id.
+ */
+ openAddonsMgr(aView, { selectTabByViewId = false } = {}) {
+ return new Promise(resolve => {
+ let emWindow;
+ let browserWindow;
+
+ const receivePong = function (aSubject) {
+ const browserWin = aSubject.browsingContext.topChromeWindow;
+ if (!emWindow || browserWin == window /* favor the current window */) {
+ if (
+ selectTabByViewId &&
+ aSubject.gViewController.currentViewId !== aView
+ ) {
+ return;
+ }
+
+ emWindow = aSubject;
+ browserWindow = browserWin;
+ }
+ };
+ Services.obs.addObserver(receivePong, "EM-pong");
+ Services.obs.notifyObservers(null, "EM-ping");
+ Services.obs.removeObserver(receivePong, "EM-pong");
+
+ if (emWindow) {
+ if (aView && !selectTabByViewId) {
+ emWindow.loadView(aView);
+ }
+ let tab = browserWindow.gBrowser.getTabForBrowser(
+ emWindow.docShell.chromeEventHandler
+ );
+ browserWindow.gBrowser.selectedTab = tab;
+ emWindow.focus();
+ resolve(emWindow);
+ return;
+ }
+
+ if (selectTabByViewId) {
+ const target = isBlankPageURL(gBrowser.currentURI.spec)
+ ? "current"
+ : "tab";
+ openTrustedLinkIn("about:addons", target);
+ } else {
+ // This must be a new load, else the ping/pong would have
+ // found the window above.
+ switchToTabHavingURI("about:addons", true);
+ }
+
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ Services.obs.removeObserver(observer, aTopic);
+ if (aView) {
+ aSubject.loadView(aView);
+ }
+ aSubject.focus();
+ resolve(aSubject);
+ }, "EM-loaded");
+ });
},
};
@@ -1551,7 +1619,7 @@ var gUnifiedExtensions = {
} else {
viewID = "addons://list/extension";
}
- await BrowserOpenAddonsMgr(viewID);
+ await BrowserAddonUI.openAddonsMgr(viewID);
return;
}
}
@@ -1877,8 +1945,6 @@ var gUnifiedExtensions = {
supportPage = null,
type = "warning",
}) {
- window.ensureCustomElements("moz-message-bar");
-
const messageBar = document.createElement("moz-message-bar");
messageBar.setAttribute("type", type);
messageBar.classList.add("unified-extensions-message-bar");
@@ -1886,8 +1952,6 @@ var gUnifiedExtensions = {
messageBar.setAttribute("data-l10n-attrs", "heading, message");
if (supportPage) {
- window.ensureCustomElements("moz-support-link");
-
const supportUrl = document.createElement("a", {
is: "moz-support-link",
});
diff --git a/browser/base/content/browser-allTabsMenu.inc.xhtml b/browser/base/content/browser-allTabsMenu.inc.xhtml
index 71d26288f7..1be6576605 100644
--- a/browser/base/content/browser-allTabsMenu.inc.xhtml
+++ b/browser/base/content/browser-allTabsMenu.inc.xhtml
@@ -9,6 +9,10 @@
class="subviewbutton"
oncommand="gTabsPanel.searchTabs();"
data-l10n-id="all-tabs-menu-search-tabs"/>
+ <toolbarbutton id="allTabsMenu-closeDuplicateTabs"
+ class="subviewbutton"
+ oncommand="gBrowser.removeAllDuplicateTabs();"
+ data-l10n-id="all-tabs-menu-close-duplicate-tabs"/>
<toolbarbutton id="allTabsMenu-containerTabsButton"
class="subviewbutton subviewbutton-nav"
closemenu="none"
diff --git a/browser/base/content/browser-allTabsMenu.js b/browser/base/content/browser-allTabsMenu.js
index a9caeffca0..f4b15bc9c3 100644
--- a/browser/base/content/browser-allTabsMenu.js
+++ b/browser/base/content/browser-allTabsMenu.js
@@ -6,6 +6,7 @@
/* eslint-env mozilla/browser-window */
ChromeUtils.defineESModuleGetters(this, {
+ BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.sys.mjs",
TabsPanel: "resource:///modules/TabsList.sys.mjs",
});
@@ -72,6 +73,16 @@ var gTabsPanel = {
!hasHiddenTabs;
document.getElementById("allTabsMenu-hiddenTabsSeparator").hidden =
!hasHiddenTabs;
+
+ let closeDuplicateEnabled = Services.prefs.getBoolPref(
+ "browser.tabs.context.close-duplicate.enabled"
+ );
+ let closeDuplicateTabsItem = document.getElementById(
+ "allTabsMenu-closeDuplicateTabs"
+ );
+ closeDuplicateTabsItem.hidden = !closeDuplicateEnabled;
+ closeDuplicateTabsItem.disabled =
+ !closeDuplicateEnabled || !gBrowser.getAllDuplicateTabsToClose().length;
});
this.allTabsView.addEventListener("ViewShown", () =>
@@ -149,6 +160,10 @@ var gTabsPanel = {
entrypoint,
1
);
+ BrowserUsageTelemetry.recordInteractionEvent(
+ entrypoint,
+ "all-tabs-panel-entrypoint"
+ );
PanelUI.showSubView(
this.kElements.allTabsView,
this.allTabsButton,
diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml
index 41258d81bb..b030891144 100644
--- a/browser/base/content/browser-box.inc.xhtml
+++ b/browser/base/content/browser-box.inc.xhtml
@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<hbox flex="1" id="browser">
- <html:sidebar-launcher id="sidebar-launcher" flex="1" hidden="true"></html:sidebar-launcher>
+ <html:sidebar-main id="sidebar-main" flex="1" hidden="true"></html:sidebar-main>
<vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
<box id="sidebar-header" align="center">
<toolbarbutton id="sidebar-switcher-target" class="tabbable" aria-expanded="false">
@@ -13,7 +13,7 @@
</toolbarbutton>
<image id="sidebar-throbber"/>
<spacer id="sidebar-spacer"/>
- <toolbarbutton id="sidebar-close" class="close-icon tabbable" data-l10n-id="sidebar-close-button" oncommand="SidebarUI.hide();"/>
+ <toolbarbutton id="sidebar-close" class="close-icon tabbable" data-l10n-id="sidebar-close-button" oncommand="SidebarController.hide();"/>
</box>
<browser id="sidebar" autoscroll="false" disablehistory="true" disablefullscreen="true" tooltip="aHTMLTooltip"/>
</vbox>
diff --git a/browser/base/content/browser-commands.js b/browser/base/content/browser-commands.js
index 40eb4d5baa..d80f9588cd 100644
--- a/browser/base/content/browser-commands.js
+++ b/browser/base/content/browser-commands.js
@@ -295,7 +295,7 @@ var BrowserCommands = {
let where = "tab";
if (event) {
- where = whereToOpenLink(event, false, true);
+ where = BrowserUtils.whereToOpenLink(event, false, true);
switch (where) {
case "tab":
@@ -573,4 +573,19 @@ var BrowserCommands = {
fullScreen() {
window.fullScreen = !window.fullScreen || BrowserHandler.kiosk;
},
+
+ downloadsUI() {
+ if (PrivateBrowsingUtils.isWindowPrivate(window)) {
+ openTrustedLinkIn("about:downloads", "tab");
+ } else {
+ PlacesCommandHook.showPlacesOrganizer("Downloads");
+ }
+ },
+
+ forceEncodingDetection() {
+ gBrowser.selectedBrowser.forceEncodingDetection();
+ BrowserCommands.reloadWithFlags(
+ Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE
+ );
+ },
};
diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js
new file mode 100644
index 0000000000..0717ce2138
--- /dev/null
+++ b/browser/base/content/browser-init.js
@@ -0,0 +1,1107 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+let _resolveDelayedStartup;
+var delayedStartupPromise = new Promise(resolve => {
+ _resolveDelayedStartup = resolve;
+});
+
+var gBrowserInit = {
+ delayedStartupFinished: false,
+ domContentLoaded: false,
+
+ _tabToAdopt: undefined,
+ _firstContentWindowPaintDeferred: Promise.withResolvers(),
+ idleTasksFinished: Promise.withResolvers(),
+
+ _setupFirstContentWindowPaintPromise() {
+ let lastTransactionId = window.windowUtils.lastTransactionId;
+ let layerTreeListener = () => {
+ if (this.getTabToAdopt()) {
+ // Need to wait until we finish adopting the tab, or we might end
+ // up focusing the initial browser and then losing focus when it
+ // gets swapped out for the tab to adopt.
+ return;
+ }
+ removeEventListener("MozLayerTreeReady", layerTreeListener);
+ let listener = e => {
+ if (e.transactionId > lastTransactionId) {
+ window.removeEventListener("MozAfterPaint", listener);
+ this._firstContentWindowPaintDeferred.resolve();
+ }
+ };
+ addEventListener("MozAfterPaint", listener);
+ };
+ addEventListener("MozLayerTreeReady", layerTreeListener);
+ },
+
+ getTabToAdopt() {
+ if (this._tabToAdopt !== undefined) {
+ return this._tabToAdopt;
+ }
+
+ if (window.arguments && window.XULElement.isInstance(window.arguments[0])) {
+ this._tabToAdopt = window.arguments[0];
+
+ // Clear the reference of the tab being adopted from the arguments.
+ window.arguments[0] = null;
+ } else {
+ // There was no tab to adopt in the arguments, set _tabToAdopt to null
+ // to avoid checking it again.
+ this._tabToAdopt = null;
+ }
+
+ return this._tabToAdopt;
+ },
+
+ _clearTabToAdopt() {
+ this._tabToAdopt = null;
+ },
+
+ // Used to check if the new window is still adopting an existing tab as its first tab
+ // (e.g. from the WebExtensions internals).
+ isAdoptingTab() {
+ return !!this.getTabToAdopt();
+ },
+
+ onBeforeInitialXULLayout() {
+ this._setupFirstContentWindowPaintPromise();
+
+ updateBookmarkToolbarVisibility();
+
+ // Set a sane starting width/height for all resolutions on new profiles.
+ if (ChromeUtils.shouldResistFingerprinting("RoundWindowSize", null)) {
+ // When the fingerprinting resistance is enabled, making sure that we don't
+ // have a maximum window to interfere with generating rounded window dimensions.
+ document.documentElement.setAttribute("sizemode", "normal");
+ } else if (!document.documentElement.hasAttribute("width")) {
+ const TARGET_WIDTH = 1280;
+ const TARGET_HEIGHT = 1040;
+ let width = Math.min(screen.availWidth * 0.9, TARGET_WIDTH);
+ let height = Math.min(screen.availHeight * 0.9, TARGET_HEIGHT);
+
+ document.documentElement.setAttribute("width", width);
+ document.documentElement.setAttribute("height", height);
+
+ if (width < TARGET_WIDTH && height < TARGET_HEIGHT) {
+ document.documentElement.setAttribute("sizemode", "maximized");
+ }
+ }
+ if (AppConstants.MENUBAR_CAN_AUTOHIDE) {
+ const toolbarMenubar = document.getElementById("toolbar-menubar");
+ // set a default value
+ if (!toolbarMenubar.hasAttribute("autohide")) {
+ toolbarMenubar.setAttribute("autohide", true);
+ }
+ document.l10n.setAttributes(
+ toolbarMenubar,
+ "toolbar-context-menu-menu-bar-cmd"
+ );
+ toolbarMenubar.setAttribute("data-l10n-attrs", "toolbarname");
+ }
+
+ // Run menubar initialization first, to avoid TabsInTitlebar code picking
+ // up mutations from it and causing a reflow.
+ AutoHideMenubar.init();
+ // Update the chromemargin attribute so the window can be sized correctly.
+ window.TabBarVisibility.update();
+ TabsInTitlebar.init();
+
+ new LightweightThemeConsumer(document);
+
+ if (
+ Services.prefs.getBoolPref(
+ "toolkit.legacyUserProfileCustomizations.windowIcon",
+ false
+ )
+ ) {
+ document.documentElement.setAttribute("icon", "main-window");
+ }
+
+ // Call this after we set attributes that might change toolbars' computed
+ // text color.
+ ToolbarIconColor.init();
+ },
+
+ onDOMContentLoaded() {
+ // This needs setting up before we create the first remote browser.
+ window.docShell.treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIAppWindow).XULBrowserWindow = window.XULBrowserWindow;
+ window.browserDOMWindow = new nsBrowserAccess();
+
+ gBrowser = window._gBrowser;
+ delete window._gBrowser;
+ gBrowser.init();
+
+ BrowserWindowTracker.track(window);
+
+ FirefoxViewHandler.init();
+
+ gNavToolbox.palette = document.getElementById(
+ "BrowserToolbarPalette"
+ ).content;
+ for (let area of CustomizableUI.areas) {
+ let type = CustomizableUI.getAreaType(area);
+ if (type == CustomizableUI.TYPE_TOOLBAR) {
+ let node = document.getElementById(area);
+ CustomizableUI.registerToolbarNode(node);
+ }
+ }
+ BrowserSearch.initPlaceHolder();
+
+ // Hack to ensure that the various initial pages favicon is loaded
+ // instantaneously, to avoid flickering and improve perceived performance.
+ this._callWithURIToLoad(uriToLoad => {
+ let url;
+ try {
+ url = Services.io.newURI(uriToLoad);
+ } catch (e) {
+ return;
+ }
+ let nonQuery = url.prePath + url.filePath;
+ if (nonQuery in gPageIcons) {
+ gBrowser.setIcon(gBrowser.selectedTab, gPageIcons[nonQuery]);
+ }
+ });
+
+ updateFxaToolbarMenu(gFxaToolbarEnabled, true);
+
+ updatePrintCommands(gPrintEnabled);
+
+ gUnifiedExtensions.init();
+
+ // Setting the focus will cause a style flush, it's preferable to call anything
+ // that will modify the DOM from within this function before this call.
+ this._setInitialFocus();
+
+ this.domContentLoaded = true;
+ },
+
+ onLoad() {
+ gBrowser.addEventListener("DOMUpdateBlockedPopups", gPopupBlockerObserver);
+ gBrowser.addEventListener(
+ "TranslationsParent:LanguageState",
+ FullPageTranslationsPanel
+ );
+ gBrowser.addEventListener(
+ "TranslationsParent:OfferTranslation",
+ FullPageTranslationsPanel
+ );
+ gBrowser.addTabsProgressListener(FullPageTranslationsPanel);
+
+ window.addEventListener("AppCommand", HandleAppCommandEvent, true);
+
+ // These routines add message listeners. They must run before
+ // loading the frame script to ensure that we don't miss any
+ // message sent between when the frame script is loaded and when
+ // the listener is registered.
+ CaptivePortalWatcher.init();
+ ZoomUI.init(window);
+
+ if (!gMultiProcessBrowser) {
+ // There is a Content:Click message manually sent from content.
+ gBrowser.tabpanels.addEventListener("click", contentAreaClick, {
+ capture: true,
+ mozSystemGroup: true,
+ });
+ }
+
+ // hook up UI through progress listener
+ gBrowser.addProgressListener(window.XULBrowserWindow);
+ gBrowser.addTabsProgressListener(window.TabsProgressListener);
+
+ SidebarController.init();
+
+ // We do this in onload because we want to ensure the button's state
+ // doesn't flicker as the window is being shown.
+ DownloadsButton.init();
+
+ // Certain kinds of automigration rely on this notification to complete
+ // their tasks BEFORE the browser window is shown. SessionStore uses it to
+ // restore tabs into windows AFTER important parts like gMultiProcessBrowser
+ // have been initialized.
+ Services.obs.notifyObservers(window, "browser-window-before-show");
+
+ if (!window.toolbar.visible) {
+ // adjust browser UI for popups
+ gURLBar.readOnly = true;
+ }
+
+ // Misc. inits.
+ gUIDensity.init();
+ TabletModeUpdater.init();
+ CombinedStopReload.ensureInitialized();
+ gPrivateBrowsingUI.init();
+ BrowserSearch.init();
+ BrowserPageActions.init();
+ if (gToolbarKeyNavEnabled) {
+ ToolbarKeyboardNavigator.init();
+ }
+
+ // Update UI if browser is under remote control.
+ gRemoteControl.updateVisualCue();
+
+ // If we are given a tab to swap in, take care of it before first paint to
+ // avoid an about:blank flash.
+ let tabToAdopt = this.getTabToAdopt();
+ if (tabToAdopt) {
+ let evt = new CustomEvent("before-initial-tab-adopted", {
+ bubbles: true,
+ });
+ gBrowser.tabpanels.dispatchEvent(evt);
+
+ // Stop the about:blank load
+ gBrowser.stop();
+
+ // Remove the speculative focus from the urlbar to let the url be formatted.
+ gURLBar.removeAttribute("focused");
+
+ let swapBrowsers = () => {
+ try {
+ gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToAdopt);
+ } catch (e) {
+ console.error(e);
+ }
+
+ // Clear the reference to the tab once its adoption has been completed.
+ this._clearTabToAdopt();
+ };
+ if (tabToAdopt.linkedBrowser.isRemoteBrowser) {
+ // For remote browsers, wait for the paint event, otherwise the tabs
+ // are not yet ready and focus gets confused because the browser swaps
+ // out while tabs are switching.
+ addEventListener("MozAfterPaint", swapBrowsers, { once: true });
+ } else {
+ swapBrowsers();
+ }
+ }
+
+ // Wait until chrome is painted before executing code not critical to making the window visible
+ this._boundDelayedStartup = this._delayedStartup.bind(this);
+ window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
+
+ if (!PrivateBrowsingUtils.enabled) {
+ document.getElementById("Tools:PrivateBrowsing").hidden = true;
+ // Setting disabled doesn't disable the shortcut, so we just remove
+ // the keybinding.
+ document.getElementById("key_privatebrowsing").remove();
+ }
+
+ if (BrowserUIUtils.quitShortcutDisabled) {
+ document.getElementById("key_quitApplication").remove();
+ document.getElementById("menu_FileQuitItem").removeAttribute("key");
+
+ PanelMultiView.getViewNode(
+ document,
+ "appMenu-quit-button2"
+ )?.removeAttribute("key");
+ }
+
+ this._loadHandled = true;
+ },
+
+ _cancelDelayedStartup() {
+ window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
+ this._boundDelayedStartup = null;
+ },
+
+ _delayedStartup() {
+ let { TelemetryTimestamps } = ChromeUtils.importESModule(
+ "resource://gre/modules/TelemetryTimestamps.sys.mjs"
+ );
+ TelemetryTimestamps.add("delayedStartupStarted");
+
+ this._cancelDelayedStartup();
+
+ // Bug 1531854 - The hidden window is force-created here
+ // until all of its dependencies are handled.
+ Services.appShell.hiddenDOMWindow;
+
+ gBrowser.addEventListener(
+ "PermissionStateChange",
+ function () {
+ gIdentityHandler.refreshIdentityBlock();
+ gPermissionPanel.updateSharingIndicator();
+ },
+ true
+ );
+
+ this._handleURIToLoad();
+
+ Services.obs.addObserver(gIdentityHandler, "perm-changed");
+ Services.obs.addObserver(gRemoteControl, "devtools-socket");
+ Services.obs.addObserver(gRemoteControl, "marionette-listening");
+ Services.obs.addObserver(gRemoteControl, "remote-listening");
+ Services.obs.addObserver(
+ gSessionHistoryObserver,
+ "browser:purge-session-history"
+ );
+ Services.obs.addObserver(
+ gStoragePressureObserver,
+ "QuotaManager::StoragePressure"
+ );
+ Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled");
+ Services.obs.addObserver(gXPInstallObserver, "addon-install-started");
+ Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked");
+ Services.obs.addObserver(
+ gXPInstallObserver,
+ "addon-install-fullscreen-blocked"
+ );
+ Services.obs.addObserver(
+ gXPInstallObserver,
+ "addon-install-origin-blocked"
+ );
+ Services.obs.addObserver(
+ gXPInstallObserver,
+ "addon-install-policy-blocked"
+ );
+ Services.obs.addObserver(
+ gXPInstallObserver,
+ "addon-install-webapi-blocked"
+ );
+ Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
+ Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation");
+ Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
+
+ BrowserOffline.init();
+ CanvasPermissionPromptHelper.init();
+ WebAuthnPromptHelper.init();
+ ContentAnalysis.initialize(document);
+
+ // Initialize the full zoom setting.
+ // We do this before the session restore service gets initialized so we can
+ // apply full zoom settings to tabs restored by the session restore service.
+ FullZoom.init();
+ PanelUI.init(shouldSuppressPopupNotifications);
+ ReportBrokenSite.init(gBrowser);
+
+ UpdateUrlbarSearchSplitterState();
+
+ BookmarkingUI.init();
+ BrowserSearch.delayedStartupInit();
+ SearchUIUtils.init();
+ gProtectionsHandler.init();
+ HomePage.delayedStartup().catch(console.error);
+
+ let safeMode = document.getElementById("helpSafeMode");
+ if (Services.appinfo.inSafeMode) {
+ document.l10n.setAttributes(safeMode, "menu-help-exit-troubleshoot-mode");
+ safeMode.setAttribute(
+ "appmenu-data-l10n-id",
+ "appmenu-help-exit-troubleshoot-mode"
+ );
+ }
+
+ // BiDi UI
+ gBidiUI = isBidiEnabled();
+ if (gBidiUI) {
+ document.getElementById("documentDirection-separator").hidden = false;
+ document.getElementById("documentDirection-swap").hidden = false;
+ document.getElementById("textfieldDirection-separator").hidden = false;
+ document.getElementById("textfieldDirection-swap").hidden = false;
+ }
+
+ // Setup click-and-hold gestures access to the session history
+ // menus if global click-and-hold isn't turned on
+ if (!Services.prefs.getBoolPref("ui.click_hold_context_menus", false)) {
+ SetClickAndHoldHandlers();
+ }
+
+ function initBackForwardButtonTooltip(tooltipId, l10nId, shortcutId) {
+ let shortcut = document.getElementById(shortcutId);
+ shortcut = ShortcutUtils.prettifyShortcut(shortcut);
+
+ let tooltip = document.getElementById(tooltipId);
+ document.l10n.setAttributes(tooltip, l10nId, { shortcut });
+ }
+
+ initBackForwardButtonTooltip(
+ "back-button-tooltip-description",
+ "navbar-tooltip-back-2",
+ "goBackKb"
+ );
+
+ initBackForwardButtonTooltip(
+ "forward-button-tooltip-description",
+ "navbar-tooltip-forward-2",
+ "goForwardKb"
+ );
+
+ PlacesToolbarHelper.init();
+
+ ctrlTab.readPref();
+ Services.prefs.addObserver(ctrlTab.prefName, ctrlTab);
+
+ // The object handling the downloads indicator is initialized here in the
+ // delayed startup function, but the actual indicator element is not loaded
+ // unless there are downloads to be displayed.
+ DownloadsButton.initializeIndicator();
+
+ if (AppConstants.platform != "macosx") {
+ updateEditUIVisibility();
+ let placesContext = document.getElementById("placesContext");
+ placesContext.addEventListener("popupshowing", updateEditUIVisibility);
+ placesContext.addEventListener("popuphiding", updateEditUIVisibility);
+ }
+
+ FullScreen.init();
+ MenuTouchModeObserver.init();
+
+ if (AppConstants.MOZ_DATA_REPORTING) {
+ gDataNotificationInfoBar.init();
+ }
+
+ if (!AppConstants.MOZILLA_OFFICIAL) {
+ DevelopmentHelpers.init();
+ }
+
+ gExtensionsNotifications.init();
+
+ let wasMinimized = window.windowState == window.STATE_MINIMIZED;
+ window.addEventListener("sizemodechange", () => {
+ let isMinimized = window.windowState == window.STATE_MINIMIZED;
+ if (wasMinimized != isMinimized) {
+ wasMinimized = isMinimized;
+ UpdatePopupNotificationsVisibility();
+ }
+ });
+
+ window.addEventListener("mousemove", MousePosTracker);
+ window.addEventListener("dragover", MousePosTracker);
+
+ gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
+ gNavToolbox.addEventListener("aftercustomization", CustomizationHandler);
+
+ SessionStore.promiseInitialized.then(() => {
+ // Bail out if the window has been closed in the meantime.
+ if (window.closed) {
+ return;
+ }
+
+ // Enable the Restore Last Session command if needed
+ RestoreLastSessionObserver.init();
+
+ SidebarController.startDelayedLoad();
+
+ PanicButtonNotifier.init();
+ });
+
+ if (BrowserHandler.kiosk) {
+ // We don't modify popup windows for kiosk mode
+ if (!gURLBar.readOnly) {
+ window.fullScreen = true;
+ }
+ }
+
+ if (Services.policies.status === Services.policies.ACTIVE) {
+ if (!Services.policies.isAllowed("hideShowMenuBar")) {
+ document
+ .getElementById("toolbar-menubar")
+ .removeAttribute("toolbarname");
+ }
+ if (!Services.policies.isAllowed("filepickers")) {
+ let savePageCommand = document.getElementById("Browser:SavePage");
+ let openFileCommand = document.getElementById("Browser:OpenFile");
+
+ savePageCommand.setAttribute("disabled", "true");
+ openFileCommand.setAttribute("disabled", "true");
+
+ document.addEventListener("FilePickerBlocked", function (event) {
+ let browser = event.target;
+
+ let notificationBox = browser
+ .getTabBrowser()
+ ?.getNotificationBox(browser);
+
+ // Prevent duplicate notifications
+ if (
+ notificationBox &&
+ !notificationBox.getNotificationWithValue("filepicker-blocked")
+ ) {
+ notificationBox.appendNotification("filepicker-blocked", {
+ label: {
+ "l10n-id": "filepicker-blocked-infobar",
+ },
+ priority: notificationBox.PRIORITY_INFO_LOW,
+ });
+ }
+ });
+ }
+ let policies = Services.policies.getActivePolicies();
+ if ("ManagedBookmarks" in policies) {
+ let managedBookmarks = policies.ManagedBookmarks;
+ let children = managedBookmarks.filter(
+ child => !("toplevel_name" in child)
+ );
+ if (children.length) {
+ let managedBookmarksButton =
+ document.createXULElement("toolbarbutton");
+ managedBookmarksButton.setAttribute("id", "managed-bookmarks");
+ managedBookmarksButton.setAttribute("class", "bookmark-item");
+ let toplevel = managedBookmarks.find(
+ element => "toplevel_name" in element
+ );
+ if (toplevel) {
+ managedBookmarksButton.setAttribute(
+ "label",
+ toplevel.toplevel_name
+ );
+ } else {
+ document.l10n.setAttributes(
+ managedBookmarksButton,
+ "managed-bookmarks"
+ );
+ }
+ managedBookmarksButton.setAttribute("context", "placesContext");
+ managedBookmarksButton.setAttribute("container", "true");
+ managedBookmarksButton.setAttribute("removable", "false");
+ managedBookmarksButton.setAttribute("type", "menu");
+
+ let managedBookmarksPopup = document.createXULElement("menupopup");
+ managedBookmarksPopup.setAttribute("id", "managed-bookmarks-popup");
+ managedBookmarksPopup.setAttribute(
+ "oncommand",
+ "PlacesToolbarHelper.openManagedBookmark(event);"
+ );
+ managedBookmarksPopup.setAttribute(
+ "ondragover",
+ "event.dataTransfer.effectAllowed='none';"
+ );
+ managedBookmarksPopup.setAttribute(
+ "ondragstart",
+ "PlacesToolbarHelper.onDragStartManaged(event);"
+ );
+ managedBookmarksPopup.setAttribute(
+ "onpopupshowing",
+ "PlacesToolbarHelper.populateManagedBookmarks(this);"
+ );
+ managedBookmarksPopup.setAttribute("placespopup", "true");
+ managedBookmarksPopup.setAttribute("is", "places-popup");
+ managedBookmarksPopup.classList.add("toolbar-menupopup");
+ managedBookmarksButton.appendChild(managedBookmarksPopup);
+
+ gNavToolbox.palette.appendChild(managedBookmarksButton);
+
+ CustomizableUI.ensureWidgetPlacedInWindow(
+ "managed-bookmarks",
+ window
+ );
+
+ // Add button if it doesn't exist
+ if (!CustomizableUI.getPlacementOfWidget("managed-bookmarks")) {
+ CustomizableUI.addWidgetToArea(
+ "managed-bookmarks",
+ CustomizableUI.AREA_BOOKMARKS,
+ 0
+ );
+ }
+ }
+ }
+ }
+
+ CaptivePortalWatcher.delayedStartup();
+
+ ShoppingSidebarManager.ensureInitialized();
+
+ SessionStore.promiseAllWindowsRestored.then(() => {
+ this._schedulePerWindowIdleTasks();
+ document.documentElement.setAttribute("sessionrestored", "true");
+ });
+
+ this.delayedStartupFinished = true;
+ _resolveDelayedStartup();
+ Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
+ TelemetryTimestamps.add("delayedStartupFinished");
+ // We've announced that delayed startup has finished. Do not add code past this point.
+ },
+
+ /**
+ * Resolved on the first MozLayerTreeReady and next MozAfterPaint in the
+ * parent process.
+ */
+ get firstContentWindowPaintPromise() {
+ return this._firstContentWindowPaintDeferred.promise;
+ },
+
+ _setInitialFocus() {
+ let initiallyFocusedElement = document.commandDispatcher.focusedElement;
+
+ // To prevent startup flicker, the urlbar has the 'focused' attribute set
+ // by default. If we are not sure the urlbar will be focused in this
+ // window, we need to remove the attribute before first paint.
+ // TODO (bug 1629956): The urlbar having the 'focused' attribute by default
+ // isn't a useful optimization anymore since UrlbarInput needs layout
+ // information to focus the urlbar properly.
+ let shouldRemoveFocusedAttribute = true;
+
+ this._callWithURIToLoad(uriToLoad => {
+ if (
+ isBlankPageURL(uriToLoad) ||
+ uriToLoad == "about:privatebrowsing" ||
+ this.getTabToAdopt()?.isEmpty
+ ) {
+ gURLBar.select();
+ shouldRemoveFocusedAttribute = false;
+ return;
+ }
+
+ // If the initial browser is remote, in order to optimize for first paint,
+ // we'll defer switching focus to that browser until it has painted.
+ // Otherwise use a regular promise to guarantee that mutationobserver
+ // microtasks that could affect focusability have run.
+ let promise = gBrowser.selectedBrowser.isRemoteBrowser
+ ? this.firstContentWindowPaintPromise
+ : Promise.resolve();
+
+ promise.then(() => {
+ // If focus didn't move while we were waiting, we're okay to move to
+ // the browser.
+ if (
+ document.commandDispatcher.focusedElement == initiallyFocusedElement
+ ) {
+ gBrowser.selectedBrowser.focus();
+ }
+ });
+ });
+
+ // Delay removing the attribute using requestAnimationFrame to avoid
+ // invalidating styles multiple times in a row if uriToLoadPromise
+ // resolves before first paint.
+ if (shouldRemoveFocusedAttribute) {
+ window.requestAnimationFrame(() => {
+ if (shouldRemoveFocusedAttribute) {
+ gURLBar.removeAttribute("focused");
+ }
+ });
+ }
+ },
+
+ _handleURIToLoad() {
+ this._callWithURIToLoad(uriToLoad => {
+ if (!uriToLoad) {
+ // We don't check whether window.arguments[5] (userContextId) is set
+ // because tabbrowser.js takes care of that for the initial tab.
+ return;
+ }
+
+ // We don't check if uriToLoad is a XULElement because this case has
+ // already been handled before first paint, and the argument cleared.
+ if (Array.isArray(uriToLoad)) {
+ // This function throws for certain malformed URIs, so use exception handling
+ // so that we don't disrupt startup
+ try {
+ gBrowser.loadTabs(uriToLoad, {
+ inBackground: false,
+ replace: true,
+ // See below for the semantics of window.arguments. Only the minimum is supported.
+ userContextId: window.arguments[5],
+ triggeringPrincipal:
+ window.arguments[8] ||
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ allowInheritPrincipal: window.arguments[9],
+ csp: window.arguments[10],
+ fromExternal: true,
+ });
+ } catch (e) {}
+ } else if (window.arguments.length >= 3) {
+ // window.arguments[1]: extraOptions (nsIPropertyBag)
+ // [2]: referrerInfo (nsIReferrerInfo)
+ // [3]: postData (nsIInputStream)
+ // [4]: allowThirdPartyFixup (bool)
+ // [5]: userContextId (int)
+ // [6]: originPrincipal (nsIPrincipal)
+ // [7]: originStoragePrincipal (nsIPrincipal)
+ // [8]: triggeringPrincipal (nsIPrincipal)
+ // [9]: allowInheritPrincipal (bool)
+ // [10]: csp (nsIContentSecurityPolicy)
+ // [11]: nsOpenWindowInfo
+ let userContextId =
+ window.arguments[5] != undefined
+ ? window.arguments[5]
+ : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
+
+ let hasValidUserGestureActivation = undefined;
+ let fromExternal = undefined;
+ let globalHistoryOptions = undefined;
+ let triggeringRemoteType = undefined;
+ let forceAllowDataURI = false;
+ let wasSchemelessInput = false;
+ if (window.arguments[1]) {
+ if (!(window.arguments[1] instanceof Ci.nsIPropertyBag2)) {
+ throw new Error(
+ "window.arguments[1] must be null or Ci.nsIPropertyBag2!"
+ );
+ }
+
+ let extraOptions = window.arguments[1];
+ if (extraOptions.hasKey("hasValidUserGestureActivation")) {
+ hasValidUserGestureActivation = extraOptions.getPropertyAsBool(
+ "hasValidUserGestureActivation"
+ );
+ }
+ if (extraOptions.hasKey("fromExternal")) {
+ fromExternal = extraOptions.getPropertyAsBool("fromExternal");
+ }
+ if (extraOptions.hasKey("triggeringSponsoredURL")) {
+ globalHistoryOptions = {
+ triggeringSponsoredURL: extraOptions.getPropertyAsACString(
+ "triggeringSponsoredURL"
+ ),
+ };
+ if (extraOptions.hasKey("triggeringSponsoredURLVisitTimeMS")) {
+ globalHistoryOptions.triggeringSponsoredURLVisitTimeMS =
+ extraOptions.getPropertyAsUint64(
+ "triggeringSponsoredURLVisitTimeMS"
+ );
+ }
+ }
+ if (extraOptions.hasKey("triggeringRemoteType")) {
+ triggeringRemoteType = extraOptions.getPropertyAsACString(
+ "triggeringRemoteType"
+ );
+ }
+ if (extraOptions.hasKey("forceAllowDataURI")) {
+ forceAllowDataURI =
+ extraOptions.getPropertyAsBool("forceAllowDataURI");
+ }
+ if (extraOptions.hasKey("wasSchemelessInput")) {
+ wasSchemelessInput =
+ extraOptions.getPropertyAsBool("wasSchemelessInput");
+ }
+ }
+
+ try {
+ openLinkIn(uriToLoad, "current", {
+ referrerInfo: window.arguments[2] || null,
+ postData: window.arguments[3] || null,
+ allowThirdPartyFixup: window.arguments[4] || false,
+ userContextId,
+ // pass the origin principal (if any) and force its use to create
+ // an initial about:blank viewer if present:
+ originPrincipal: window.arguments[6],
+ originStoragePrincipal: window.arguments[7],
+ triggeringPrincipal: window.arguments[8],
+ // TODO fix allowInheritPrincipal to default to false.
+ // Default to true unless explicitly set to false because of bug 1475201.
+ allowInheritPrincipal: window.arguments[9] !== false,
+ csp: window.arguments[10],
+ forceAboutBlankViewerInCurrent: !!window.arguments[6],
+ forceAllowDataURI,
+ hasValidUserGestureActivation,
+ fromExternal,
+ globalHistoryOptions,
+ triggeringRemoteType,
+ wasSchemelessInput,
+ });
+ } catch (e) {
+ console.error(e);
+ }
+
+ window.focus();
+ } else {
+ // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
+ // Such callers expect that window.arguments[0] is handled as a single URI.
+ loadOneOrMoreURIs(
+ uriToLoad,
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null
+ );
+ }
+ });
+ },
+
+ /**
+ * Use this function as an entry point to schedule tasks that
+ * need to run once per window after startup, and can be scheduled
+ * by using an idle callback.
+ *
+ * The functions scheduled here will fire from idle callbacks
+ * once every window has finished being restored by session
+ * restore, and after the equivalent only-once tasks
+ * have run (from _scheduleStartupIdleTasks in BrowserGlue.sys.mjs).
+ */
+ _schedulePerWindowIdleTasks() {
+ // Bail out if the window has been closed in the meantime.
+ if (window.closed) {
+ return;
+ }
+
+ function scheduleIdleTask(func, options) {
+ requestIdleCallback(function idleTaskRunner() {
+ if (!window.closed) {
+ func();
+ }
+ }, options);
+ }
+
+ scheduleIdleTask(() => {
+ // Initialize the Sync UI
+ gSync.init();
+ });
+
+ scheduleIdleTask(() => {
+ // Read prefers-reduced-motion setting
+ let reduceMotionQuery = window.matchMedia(
+ "(prefers-reduced-motion: reduce)"
+ );
+ function readSetting() {
+ gReduceMotionSetting = reduceMotionQuery.matches;
+ }
+ reduceMotionQuery.addListener(readSetting);
+ readSetting();
+ });
+
+ scheduleIdleTask(() => {
+ // setup simple gestures support
+ gGestureSupport.init(true);
+
+ // setup history swipe animation
+ gHistorySwipeAnimation.init();
+ });
+
+ scheduleIdleTask(() => {
+ gBrowserThumbnails.init();
+ });
+
+ scheduleIdleTask(
+ () => {
+ // Initialize the download manager some time after the app starts so that
+ // auto-resume downloads begin (such as after crashing or quitting with
+ // active downloads) and speeds up the first-load of the download manager UI.
+ // If the user manually opens the download manager before the timeout, the
+ // downloads will start right away, and initializing again won't hurt.
+ try {
+ DownloadsCommon.initializeAllDataLinks();
+ ChromeUtils.importESModule(
+ "resource:///modules/DownloadsTaskbar.sys.mjs"
+ ).DownloadsTaskbar.registerIndicator(window);
+ if (AppConstants.platform == "macosx") {
+ ChromeUtils.importESModule(
+ "resource:///modules/DownloadsMacFinderProgress.sys.mjs"
+ ).DownloadsMacFinderProgress.register();
+ }
+ Services.telemetry.setEventRecordingEnabled("downloads", true);
+ } catch (ex) {
+ console.error(ex);
+ }
+ },
+ { timeout: 10000 }
+ );
+
+ if (Win7Features) {
+ scheduleIdleTask(() => Win7Features.onOpenWindow());
+ }
+
+ scheduleIdleTask(async () => {
+ NewTabPagePreloading.maybeCreatePreloadedBrowser(window);
+ });
+
+ scheduleIdleTask(() => {
+ gGfxUtils.init();
+ });
+
+ // This should always go last, since the idle tasks (except for the ones with
+ // timeouts) should execute in order. Note that this observer notification is
+ // not guaranteed to fire, since the window could close before we get here.
+ scheduleIdleTask(() => {
+ this.idleTasksFinished.resolve();
+ Services.obs.notifyObservers(
+ window,
+ "browser-idle-startup-tasks-finished"
+ );
+ });
+
+ scheduleIdleTask(() => {
+ gProfiles.init();
+ });
+ },
+
+ // Returns the URI(s) to load at startup if it is immediately known, or a
+ // promise resolving to the URI to load.
+ get uriToLoadPromise() {
+ delete this.uriToLoadPromise;
+ return (this.uriToLoadPromise = (function () {
+ // window.arguments[0]: URI to load (string), or an nsIArray of
+ // nsISupportsStrings to load, or a xul:tab of
+ // a tabbrowser, which will be replaced by this
+ // window (for this case, all other arguments are
+ // ignored).
+ let uri = window.arguments?.[0];
+ if (!uri || window.XULElement.isInstance(uri)) {
+ return null;
+ }
+
+ let defaultArgs = BrowserHandler.defaultArgs;
+
+ // If the given URI is different from the homepage, we want to load it.
+ if (uri != defaultArgs) {
+ AboutNewTab.noteNonDefaultStartup();
+
+ if (uri instanceof Ci.nsIArray) {
+ // Transform the nsIArray of nsISupportsString's into a JS Array of
+ // JS strings.
+ return Array.from(
+ uri.enumerate(Ci.nsISupportsString),
+ supportStr => supportStr.data
+ );
+ } else if (uri instanceof Ci.nsISupportsString) {
+ return uri.data;
+ }
+ return uri;
+ }
+
+ // The URI appears to be the the homepage. We want to load it only if
+ // session restore isn't about to override the homepage.
+ let willOverride = SessionStartup.willOverrideHomepage;
+ if (typeof willOverride == "boolean") {
+ return willOverride ? null : uri;
+ }
+ return willOverride.then(willOverrideHomepage =>
+ willOverrideHomepage ? null : uri
+ );
+ })());
+ },
+
+ // Calls the given callback with the URI to load at startup.
+ // Synchronously if possible, or after uriToLoadPromise resolves otherwise.
+ _callWithURIToLoad(callback) {
+ let uriToLoad = this.uriToLoadPromise;
+ if (uriToLoad && uriToLoad.then) {
+ uriToLoad.then(callback);
+ } else {
+ callback(uriToLoad);
+ }
+ },
+
+ onUnload() {
+ gUIDensity.uninit();
+
+ TabsInTitlebar.uninit();
+
+ ToolbarIconColor.uninit();
+
+ // In certain scenarios it's possible for unload to be fired before onload,
+ // (e.g. if the window is being closed after browser.js loads but before the
+ // load completes). In that case, there's nothing to do here.
+ if (!this._loadHandled) {
+ return;
+ }
+
+ // First clean up services initialized in gBrowserInit.onLoad (or those whose
+ // uninit methods don't depend on the services having been initialized).
+
+ CombinedStopReload.uninit();
+
+ gGestureSupport.init(false);
+
+ gHistorySwipeAnimation.uninit();
+
+ FullScreen.uninit();
+
+ gSync.uninit();
+
+ gExtensionsNotifications.uninit();
+ gUnifiedExtensions.uninit();
+
+ try {
+ gBrowser.removeProgressListener(window.XULBrowserWindow);
+ gBrowser.removeTabsProgressListener(window.TabsProgressListener);
+ } catch (ex) {}
+
+ PlacesToolbarHelper.uninit();
+
+ BookmarkingUI.uninit();
+
+ TabletModeUpdater.uninit();
+
+ gTabletModePageCounter.finish();
+
+ CaptivePortalWatcher.uninit();
+
+ SidebarController.uninit();
+
+ DownloadsButton.uninit();
+
+ if (gToolbarKeyNavEnabled) {
+ ToolbarKeyboardNavigator.uninit();
+ }
+
+ BrowserSearch.uninit();
+
+ NewTabPagePreloading.removePreloadedBrowser(window);
+
+ FirefoxViewHandler.uninit();
+
+ // Now either cancel delayedStartup, or clean up the services initialized from
+ // it.
+ if (this._boundDelayedStartup) {
+ this._cancelDelayedStartup();
+ } else {
+ if (Win7Features) {
+ Win7Features.onCloseWindow();
+ }
+ Services.prefs.removeObserver(ctrlTab.prefName, ctrlTab);
+ ctrlTab.uninit();
+ gBrowserThumbnails.uninit();
+ gProtectionsHandler.uninit();
+ FullZoom.destroy();
+
+ Services.obs.removeObserver(gIdentityHandler, "perm-changed");
+ Services.obs.removeObserver(gRemoteControl, "devtools-socket");
+ Services.obs.removeObserver(gRemoteControl, "marionette-listening");
+ Services.obs.removeObserver(gRemoteControl, "remote-listening");
+ Services.obs.removeObserver(
+ gSessionHistoryObserver,
+ "browser:purge-session-history"
+ );
+ Services.obs.removeObserver(
+ gStoragePressureObserver,
+ "QuotaManager::StoragePressure"
+ );
+ Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
+ Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
+ Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
+ Services.obs.removeObserver(
+ gXPInstallObserver,
+ "addon-install-fullscreen-blocked"
+ );
+ Services.obs.removeObserver(
+ gXPInstallObserver,
+ "addon-install-origin-blocked"
+ );
+ Services.obs.removeObserver(
+ gXPInstallObserver,
+ "addon-install-policy-blocked"
+ );
+ Services.obs.removeObserver(
+ gXPInstallObserver,
+ "addon-install-webapi-blocked"
+ );
+ Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
+ Services.obs.removeObserver(
+ gXPInstallObserver,
+ "addon-install-confirmation"
+ );
+ Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
+
+ MenuTouchModeObserver.uninit();
+ BrowserOffline.uninit();
+ CanvasPermissionPromptHelper.uninit();
+ WebAuthnPromptHelper.uninit();
+ PanelUI.uninit();
+ }
+
+ // Final window teardown, do this last.
+ gBrowser.destroy();
+ window.XULBrowserWindow = null;
+ window.docShell.treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIAppWindow).XULBrowserWindow = null;
+ window.browserDOMWindow = null;
+ },
+};
+
+gBrowserInit.idleTasksFinishedPromise = gBrowserInit.idleTasksFinished.promise;
diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc
index 0c8b4a1cf6..0eebfea75a 100644
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -140,22 +140,7 @@
</menupopup>
</menu>
<menu id="viewSidebarMenuMenu" data-l10n-id="menu-view-sidebar">
- <menupopup id="viewSidebarMenu" onpopupshowing="SidebarUI.setMegalistMenubarVisibility(event);">
- <menuitem id="menu_bookmarksSidebar"
- type="checkbox"
- key="viewBookmarksSidebarKb"
- oncommand="SidebarUI.toggle('viewBookmarksSidebar');" data-l10n-id="menu-view-bookmarks"/>
- <menuitem id="menu_historySidebar"
- type="checkbox"
- key="key_gotoHistory"
- oncommand="SidebarUI.toggle('viewHistorySidebar');" data-l10n-id="menu-view-history-button"/>
- <menuitem id="menu_tabsSidebar"
- type="checkbox"
- class="sync-ui-item"
- oncommand="SidebarUI.toggle('viewTabsSidebar');" data-l10n-id="menu-view-synced-tabs-sidebar"/>
- <menuitem id="menu_megalistSidebar"
- type="checkbox"
- oncommand="SidebarUI.toggle('viewMegalistSidebar');" data-l10n-id="menu-view-megalist-sidebar"/>
+ <menupopup id="viewSidebarMenu" onpopupshowing="SidebarController.setMegalistMenubarVisibility(event);">
</menupopup>
</menu>
<menuseparator/>
@@ -192,7 +177,7 @@
</menu>
<menuitem id="repair-text-encoding"
disabled="true"
- oncommand="BrowserForceEncodingDetection();"
+ oncommand="BrowserCommands.forceEncodingDetection();"
data-l10n-id="menu-view-repair-text-encoding"/>
<menuseparator/>
#ifdef XP_MACOSX
diff --git a/browser/base/content/browser-pageActions.js b/browser/base/content/browser-pageActions.js
index 1fd1062948..fe0453160d 100644
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -970,7 +970,7 @@ var BrowserPageActions = {
this._contextAction = null;
let viewID = "addons://detail/" + encodeURIComponent(action.extensionID);
- window.BrowserOpenAddonsMgr(viewID);
+ window.BrowserAddonUI.openAddonsMgr(viewID);
},
/**
diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js
index c940fade5f..404a080983 100644
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -55,7 +55,6 @@ var StarUI = {
delete this.panel;
this._createPanelIfNeeded();
var element = this._element("editBookmarkPanel");
- window.ensureCustomElements("moz-button-group");
// initially the panel is hidden
// to avoid impacting startup / new window performance
element.hidden = false;
@@ -1378,7 +1377,7 @@ var BookmarkingUI = {
this.updateLabel(
"BMB_viewBookmarksSidebar",
- SidebarUI.currentID == "viewBookmarksSidebar"
+ SidebarController.currentID == "viewBookmarksSidebar"
);
this.updateLabel("BMB_viewBookmarksToolbar", !this.toolbar.collapsed);
},
@@ -1999,6 +1998,13 @@ var BookmarkingUI = {
case "ViewHiding":
this.onPanelMenuViewHiding(aEvent);
break;
+ case "command":
+ if (aEvent.target.id == "panelMenu_searchBookmarks") {
+ PlacesCommandHook.searchBookmarks();
+ } else if (aEvent.target.id == "panelMenu_viewBookmarksToolbar") {
+ this.toggleBookmarksToolbar("bookmark-tools");
+ }
+ break;
}
},
@@ -2026,12 +2032,15 @@ var BookmarkingUI = {
panelview
);
panelview.removeEventListener("ViewShowing", this);
+ panelview.addEventListener("command", this);
},
onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) {
this._panelMenuView.uninit();
delete this._panelMenuView;
- aEvent.target.removeEventListener("ViewHiding", this);
+ let panelview = aEvent.target;
+ panelview.removeEventListener("ViewHiding", this);
+ panelview.removeEventListener("command", this);
},
handlePlacesEvents(aEvents) {
diff --git a/browser/base/content/browser-sets.inc b/browser/base/content/browser-sets.inc
index e247f0641e..f77ce1661e 100644
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -91,8 +91,8 @@
<command id="Browser:NewUserContextTab" oncommand="openNewUserContextTab(event.sourceEvent);"/>
<command id="Browser:OpenAboutContainers" oncommand="openPreferences('paneContainers');"/>
<command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
- <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
- <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
+ <command id="Tools:Downloads" oncommand="BrowserCommands.downloadsUI();"/>
+ <command id="Tools:Addons" oncommand="BrowserAddonUI.openAddonsMgr();"/>
<command id="Tools:Sanitize" oncommand="Sanitizer.showUI(window);"/>
<command id="Tools:PrivateBrowsing"
oncommand="OpenBrowserWindow({private: true});"/>
@@ -276,7 +276,7 @@
<key id="viewBookmarksSidebarKb"
data-l10n-id="bookmark-show-sidebar-shortcut"
modifiers="accel"
- oncommand="SidebarUI.toggle('viewBookmarksSidebar');"/>
+ oncommand="SidebarController.toggle('viewBookmarksSidebar');"/>
<key id="viewBookmarksToolbarKb"
data-l10n-id="bookmark-show-toolbar-shortcut"
oncommand="BookmarkingUI.toggleBookmarksToolbar('shortcut');"
@@ -295,7 +295,7 @@
#else
modifiers="accel"
#endif
- oncommand="SidebarUI.toggle('viewHistorySidebar');"/>
+ oncommand="SidebarController.toggle('viewHistorySidebar');"/>
<key id="key_fullZoomReduce" data-l10n-id="full-zoom-reduce-shortcut" command="cmd_fullZoomReduce" modifiers="accel"/>
<key data-l10n-id="full-zoom-reduce-shortcut-alt-a" command="cmd_fullZoomReduce" modifiers="accel"/>
diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js
index 60bd4fc01c..eaed3950fe 100644
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -177,7 +177,6 @@ var gIdentityHandler = {
_popupInitialized: false,
_initializePopup() {
- window.ensureCustomElements("moz-support-link");
if (!this._popupInitialized) {
let wrapper = document.getElementById("template-identity-popup");
wrapper.replaceWith(wrapper.content);
diff --git a/browser/base/content/browser-sitePermissionPanel.js b/browser/base/content/browser-sitePermissionPanel.js
index d977653f51..de7b2cc39a 100644
--- a/browser/base/content/browser-sitePermissionPanel.js
+++ b/browser/base/content/browser-sitePermissionPanel.js
@@ -14,9 +14,6 @@ var gPermissionPanel = {
if (!this._popupInitialized) {
let wrapper = document.getElementById("template-permission-popup");
wrapper.replaceWith(wrapper.content);
-
- window.ensureCustomElements("moz-support-link");
-
this._popupInitialized = true;
}
},
diff --git a/browser/base/content/browser-siteProtections.js b/browser/base/content/browser-siteProtections.js
index 043dc53b95..31b87ac7e0 100644
--- a/browser/base/content/browser-siteProtections.js
+++ b/browser/base/content/browser-siteProtections.js
@@ -1377,7 +1377,6 @@ var gProtectionsHandler = {
let wrapper = document.getElementById("template-protections-popup");
this._protectionsPopup = wrapper.content.firstElementChild;
wrapper.replaceWith(wrapper.content);
- window.ensureCustomElements("moz-support-link");
this.maybeSetMilestoneCounterText();
@@ -1591,8 +1590,6 @@ var gProtectionsHandler = {
// Add an observer to observe that the history has been cleared.
Services.obs.addObserver(this, "browser:purge-session-history");
-
- window.ensureCustomElements("moz-button-group", "moz-toggle");
},
uninit() {
@@ -2601,14 +2598,14 @@ var gProtectionsHandler = {
_createHeroElement(doc, message) {
const messageEl = this._createElement(doc, "div");
messageEl.setAttribute("id", "protections-popup-message");
- messageEl.classList.add("whatsNew-hero-message");
+ messageEl.classList.add("protections-hero-message");
const wrapperEl = this._createElement(doc, "div");
- wrapperEl.classList.add("whatsNew-message-body");
+ wrapperEl.classList.add("protections-popup-message-body");
messageEl.appendChild(wrapperEl);
wrapperEl.appendChild(
this._createElement(doc, "h2", {
- classList: "whatsNew-message-title",
+ classList: "protections-popup-message-title",
content: message.content.title,
})
);
diff --git a/browser/base/content/browser-sync.js b/browser/base/content/browser-sync.js
index 065546d7b8..9aa6cc5cd4 100644
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -202,10 +202,7 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
}
_appendSyncClient(client, container, labelId, paginationInfo) {
- let {
- maxTabs = SyncedTabsPanelList.sRemoteTabsPerPage,
- showInactive = false,
- } = paginationInfo;
+ let { maxTabs = SyncedTabsPanelList.sRemoteTabsPerPage } = paginationInfo;
// Create the element for the remote client.
let clientItem = document.createXULElement("label");
clientItem.setAttribute("id", labelId);
@@ -227,11 +224,24 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
);
label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
} else {
- let tabs = client.tabs.filter(t => showInactive || !t.inactive);
- let numInactive = client.tabs.length - tabs.length;
+ // We have the client obj but we need the FxA device obj so we use the clients
+ // engine to get us the FxA device
+ let device =
+ fxAccounts.device.recentDeviceList &&
+ fxAccounts.device.recentDeviceList.find(
+ d =>
+ d.id === Weave.Service.clientsEngine.getClientFxaDeviceId(client.id)
+ );
+ let remoteTabCloseAvailable =
+ device && fxAccounts.commands.closeTab.isDeviceCompatible(device);
+
+ let tabs = client.tabs.filter(t => !t.inactive);
+ let hasInactive = tabs.length != client.tabs.length;
- // If this page will display all tabs, show no additional buttons.
- // Otherwise, show a "Show More" button
+ if (hasInactive) {
+ container.append(this._createShowInactiveTabsElement(client, device));
+ }
+ // If this page isn't displaying all (regular, active) tabs, show a "Show More" button.
let hasNextPage = tabs.length > maxTabs;
let nextPageIsLastPage =
hasNextPage &&
@@ -248,15 +258,13 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
tabs = tabs.slice(0, maxTabs);
}
for (let [index, tab] of tabs.entries()) {
- let tabEnt = this._createSyncedTabElement(tab, index);
- container.appendChild(tabEnt);
- }
- if (numInactive) {
- let elt = this._createShowInactiveTabsElement(
- paginationInfo,
- numInactive
+ let tabEnt = this._createSyncedTabElement(
+ tab,
+ index,
+ device,
+ remoteTabCloseAvailable
);
- container.appendChild(elt);
+ container.appendChild(tabEnt);
}
if (hasNextPage) {
let showAllEnt = this._createShowMoreSyncedTabsElement(paginationInfo);
@@ -265,7 +273,10 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
}
}
- _createSyncedTabElement(tabInfo, index) {
+ _createSyncedTabElement(tabInfo, index, device, canCloseTabs) {
+ let tabContainer = document.createXULElement("hbox");
+ tabContainer.setAttribute("class", "PanelUI-tabitem-container");
+
let item = document.createXULElement("toolbarbutton");
let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
item.setAttribute("itemtype", "tab");
@@ -296,25 +307,29 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
{}
),
});
- if (document.defaultView.whereToOpenLink(e) != "current") {
+ if (BrowserUtils.whereToOpenLink(e) != "current") {
e.preventDefault();
e.stopPropagation();
} else {
CustomizableUI.hidePanelForNode(item);
}
});
- return item;
+ tabContainer.appendChild(item);
+ // We should only add an X button next to tabs if the device
+ // is broadcasting that it can remotely close tabs
+ if (canCloseTabs) {
+ tabContainer.appendChild(
+ this._createCloseTabElement(tabInfo.url, device)
+ );
+ }
+ return tabContainer;
}
_createShowMoreSyncedTabsElement(paginationInfo) {
let showMoreItem = document.createXULElement("toolbarbutton");
showMoreItem.setAttribute("itemtype", "showmorebutton");
showMoreItem.setAttribute("closemenu", "none");
- showMoreItem.classList.add(
- "subviewbutton",
- "subviewbutton-nav",
- "subviewbutton-nav-down"
- );
+ showMoreItem.classList.add("subviewbutton", "subviewbutton-nav-down");
document.l10n.setAttributes(showMoreItem, "appmenu-remote-tabs-showmore");
paginationInfo.maxTabs = Infinity;
@@ -326,27 +341,56 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
return showMoreItem;
}
- _createShowInactiveTabsElement(paginationInfo, count) {
+ _createShowInactiveTabsElement(client, device) {
let showItem = document.createXULElement("toolbarbutton");
- showItem.setAttribute("itemtype", "showmorebutton");
showItem.setAttribute("closemenu", "none");
- showItem.classList.add(
- "subviewbutton",
- "subviewbutton-nav",
- "subviewbutton-nav-down"
+ showItem.classList.add("subviewbutton", "subviewbutton-nav");
+ document.l10n.setAttributes(
+ showItem,
+ "appmenu-remote-tabs-show-inactive-tabs"
);
- document.l10n.setAttributes(showItem, "appmenu-remote-tabs-showinactive");
- document.l10n.setArgs(showItem, { count });
- paginationInfo.showInactive = true;
+ let canClose =
+ device && fxAccounts.commands.closeTab.isDeviceCompatible(device);
+
showItem.addEventListener("click", e => {
- e.preventDefault();
- e.stopPropagation();
- this._showSyncedTabs(paginationInfo);
+ let node = PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-inactive-tabs"
+ );
+
+ // device name.
+ let label = node.querySelector("label[itemtype='client']");
+ label.textContent = client.name;
+
+ // Update the tab list.
+ let container = node.querySelector(".panel-subview-body");
+ container.replaceChildren(
+ ...client.tabs
+ .filter(t => t.inactive)
+ .map((tab, index) =>
+ this._createSyncedTabElement(tab, index, device, canClose)
+ )
+ );
+ PanelUI.showSubView("PanelUI-fxa-menu-inactive-tabs", showItem, e);
});
return showItem;
}
+ _createCloseTabElement(url, device) {
+ let closeBtn = document.createXULElement("image");
+ closeBtn.setAttribute("class", "close-icon remotetabs-close");
+
+ closeBtn.addEventListener("click", function (e) {
+ e.stopPropagation();
+ // The user could be hitting multiple tabs across multiple devices, with a few
+ // seconds in-between -- we should not immediately fire off pushes, so we
+ // add it to a queue and send in bulk at a later time
+ fxAccounts.commands.closeTab.enqueueTabToClose(device, url);
+ });
+ return closeBtn;
+ }
+
destroy() {
Services.obs.removeObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
this.tabsList = null;
@@ -384,7 +428,7 @@ var gSync = {
"browser/accounts.ftl",
"browser/appmenu.ftl",
"browser/sync.ftl",
- "toolkit/branding/accounts.ftl",
+ "browser/syncedTabs.ftl",
],
true
));
@@ -445,7 +489,7 @@ var gSync = {
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
- "PXI_TOOLBAR_ENABLED",
+ "FXA_CTA_MENU_ENABLED",
"identity.fxaccounts.toolbar.pxiToolbarEnabled"
);
},
@@ -533,10 +577,23 @@ var gSync = {
let fxaPanelView = PanelMultiView.getViewNode(document, "PanelUI-fxa");
fxaPanelView.addEventListener("ViewShowing", this);
fxaPanelView.addEventListener("ViewHiding", this);
+ fxaPanelView.addEventListener("command", this);
+ PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-syncnow-button"
+ ).addEventListener("mouseover", this);
+ PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-sendtab-not-configured-button"
+ ).addEventListener("command", this);
+ PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-sendtab-connect-device-button"
+ ).addEventListener("command", this);
// If the experiment is enabled, we'll need to update the panels
// to show some different text to the user
- if (this.PXI_TOOLBAR_ENABLED) {
+ if (this.FXA_CTA_MENU_ENABLED) {
this.updateFxAPanel(UIState.get());
this.updateCTAPanel();
}
@@ -558,6 +615,13 @@ var gSync = {
handleEvent(event) {
switch (event.type) {
+ case "mouseover":
+ this.refreshSyncButtonsTooltip();
+ break;
+ case "command": {
+ this.onCommand(event.target);
+ break;
+ }
case "ViewShowing": {
this.onFxAPanelViewShowing(event.target);
break;
@@ -606,16 +670,61 @@ var gSync = {
panelview.syncedTabsPanelList = null;
},
+ onCommand(button) {
+ switch (button.id) {
+ case "PanelUI-fxa-menu-sync-prefs-button":
+ // fall through
+ case "PanelUI-fxa-menu-setup-sync-button":
+ this.openPrefsFromFxaMenu("sync_settings", button);
+ break;
+
+ case "PanelUI-fxa-menu-sendtab-connect-device-button":
+ // fall through
+ case "PanelUI-fxa-menu-connect-device-button":
+ this.openConnectAnotherDeviceFromFxaMenu(button);
+ break;
+
+ case "fxa-manage-account-button":
+ this.clickFxAMenuHeaderButton(button);
+ break;
+ case "PanelUI-fxa-menu-syncnow-button":
+ this.doSyncFromFxaMenu(button);
+ break;
+ case "PanelUI-fxa-menu-sendtab-button":
+ this.showSendToDeviceViewFromFxaMenu(button);
+ break;
+ case "PanelUI-fxa-menu-account-signout-button":
+ this.disconnect();
+ break;
+ case "PanelUI-fxa-menu-sync-button":
+ this.openPrefsFromFxaButton("sync_cta", button);
+ break;
+ case "PanelUI-fxa-menu-monitor-button":
+ this.openMonitorLink(button);
+ break;
+ case "PanelUI-fxa-menu-relay-button":
+ this.openRelayLink(button);
+ break;
+ case "PanelUI-fxa-menu-vpn-button":
+ this.openVPNLink(button);
+ break;
+ case "PanelUI-fxa-menu-sendtab-not-configured-button":
+ this.openPrefsFromFxaMenu("send_tab", button);
+ break;
+ }
+ },
+
observe(subject, topic, data) {
if (!this._initialized) {
console.error("browser-sync observer called after unload: ", topic);
return;
}
switch (topic) {
- case UIState.ON_UPDATE:
+ case UIState.ON_UPDATE: {
const state = UIState.get();
this.updateAllUI(state);
break;
+ }
case "quit-application":
// Stop the animation timer on shutdown, since we can't update the UI
// after this.
@@ -637,7 +746,6 @@ var gSync = {
this.updateSyncButtonsTooltip(state);
this.updateSyncStatus(state);
this.updateFxAPanel(state);
- this.updateCTAPanel(state);
// Ensure we have something in the device list in the background.
this.ensureFxaDevices();
},
@@ -720,16 +828,6 @@ var gSync = {
this.emitFxaToolbarTelemetry("send_tab", anchor);
},
- showRemoteTabsFromFxaMenu(panel) {
- PanelUI.showSubView("PanelUI-remotetabs", panel);
- this.emitFxaToolbarTelemetry("sync_tabs", panel);
- },
-
- showSidebarFromFxaMenu(panel) {
- SidebarUI.toggle("viewTabsSidebar");
- this.emitFxaToolbarTelemetry("sync_tabs_sidebar", panel);
- },
-
_populateSendTabToDevicesView(panelViewNode, reloadDevices = true) {
let bodyNode = panelViewNode.querySelector(".panel-subview-body");
let panelNode = panelViewNode.closest("panel");
@@ -830,12 +928,20 @@ var gSync = {
let fxaStatus = document.documentElement.getAttribute("fxastatus");
if (fxaStatus == "not_configured") {
+ // sign in button in app (hamburger) menu
+ // should take you straight to fxa sign in page
+ if (anchor.id == "appMenu-fxa-label2") {
+ this.openFxAEmailFirstPageFromFxaMenu(anchor);
+ PanelUI.hide();
+ return;
+ }
+
// If we're signed out but have the PXI pref enabled
// we should show the PXI panel instead of taking the user
// straight to FxA sign-in
- if (this.PXI_TOOLBAR_ENABLED) {
+ if (this.FXA_CTA_MENU_ENABLED) {
this.updateFxAPanel(UIState.get());
- this.updateCTAPanel();
+ this.updateCTAPanel(anchor);
PanelUI.showSubView("PanelUI-fxa", anchor, aEvent);
} else if (anchor == document.getElementById("fxa-toolbar-menu-button")) {
// The fxa toolbar button doesn't have much context before the user
@@ -844,20 +950,13 @@ var gSync = {
this.emitFxaToolbarTelemetry("toolbar_icon", anchor);
openTrustedLinkIn("about:preferences#sync", "tab");
PanelUI.hide();
- } else {
- let panel =
- anchor.id == "appMenu-fxa-label2"
- ? PanelMultiView.getViewNode(document, "PanelUI-fxa")
- : undefined;
- this.openFxAEmailFirstPageFromFxaMenu(panel);
- PanelUI.hide();
}
return;
}
// If the user is signed in and we have the PXI pref enabled then add
// the pxi panel to the existing toolbar
- if (this.PXI_TOOLBAR_ENABLED) {
- this.updateCTAPanel();
+ if (this.FXA_CTA_MENU_ENABLED) {
+ this.updateCTAPanel(anchor);
}
if (!gFxaToolbarAccessed) {
@@ -932,21 +1031,16 @@ var gSync = {
fxaMenuAccountButtonEl.removeAttribute("closemenu");
syncSetupButtonEl.removeAttribute("hidden");
- let headerTitleL10nId = this.PXI_TOOLBAR_ENABLED
- ? "appmenuitem-sign-in-account"
- : "appmenuitem-fxa-sign-in";
+ let headerTitleL10nId = this.FXA_CTA_MENU_ENABLED
+ ? "synced-tabs-fxa-sign-in"
+ : "appmenuitem-sign-in-account";
let headerDescription;
if (state.status === UIState.STATUS_NOT_CONFIGURED) {
mainWindowEl.style.removeProperty("--avatar-image-url");
- headerDescription = this.fluentStrings.formatValueSync(
- "appmenu-fxa-signed-in-label"
- );
- // Signed out, expeirment enabled is the only state we want to hide the
- // header description, so we make it empty and check for that when setting
- // the value
- if (this.PXI_TOOLBAR_ENABLED) {
- headerDescription = "";
- }
+ const headerDescString = this.FXA_CTA_MENU_ENABLED
+ ? "fxa-menu-sync-description"
+ : "appmenu-fxa-signed-in-label";
+ headerDescription = this.fluentStrings.formatValueSync(headerDescString);
} else if (state.status === UIState.STATUS_LOGIN_FAILED) {
stateValue = "login-failed";
headerTitleL10nId = "account-disconnected2";
@@ -1020,8 +1114,8 @@ var gSync = {
).hidden = !canSendAllURIs;
},
- emitFxaToolbarTelemetry(type, panel) {
- if (UIState.isReady() && panel) {
+ emitFxaToolbarTelemetry(type, sourceElement) {
+ if (UIState.isReady() && sourceElement) {
const state = UIState.get();
const hasAvatar = state.avatarURL && !state.avatarIsDefault;
let extraOptions = {
@@ -1029,10 +1123,10 @@ var gSync = {
fxa_avatar: hasAvatar ? "true" : "false",
};
- // When the fxa avatar panel is within the Firefox app menu,
+ // When the source element is within the Firefox app menu,
// we emit different telemetry.
let eventName = "fxa_avatar_menu";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
eventName = "fxa_app_menu";
}
@@ -1046,9 +1140,9 @@ var gSync = {
}
},
- isPanelInsideAppMenu(panel = undefined) {
+ isInsideAppMenu(sourceElement = undefined) {
const appMenuPanel = document.getElementById("appMenu-popup");
- if (panel && appMenuPanel.contains(panel)) {
+ if (sourceElement && appMenuPanel.contains(sourceElement)) {
return true;
}
return false;
@@ -1225,10 +1319,10 @@ var gSync = {
openTrustedLinkIn(url, "tab");
},
- async openConnectAnotherDeviceFromFxaMenu(panel = undefined) {
- this.emitFxaToolbarTelemetry("cad", panel);
+ async openConnectAnotherDeviceFromFxaMenu(sourceElement = undefined) {
+ this.emitFxaToolbarTelemetry("cad", sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
entryPoint = "fxa_app_menu";
}
this.openConnectAnotherDevice(entryPoint);
@@ -1241,7 +1335,7 @@ var gSync = {
switchToTabHavingURI(url, true, { replaceQueryString: true });
},
- async clickFxAMenuHeaderButton(panel = undefined) {
+ async clickFxAMenuHeaderButton(sourceElement = undefined) {
// Depending on the current logged in state of a user,
// clicking the FxA header will either open
// a sign-in page, account management page, or sync
@@ -1249,16 +1343,16 @@ var gSync = {
const { status } = UIState.get();
switch (status) {
case UIState.STATUS_NOT_CONFIGURED:
- this.openFxAEmailFirstPageFromFxaMenu(panel);
+ this.openFxAEmailFirstPageFromFxaMenu(sourceElement);
break;
case UIState.STATUS_LOGIN_FAILED:
- this.openPrefsFromFxaMenu("sync_settings", panel);
+ this.openPrefsFromFxaMenu("sync_settings", sourceElement);
break;
case UIState.STATUS_NOT_VERIFIED:
this.openFxAEmailFirstPage("fxa_app_menu_reverify");
break;
case UIState.STATUS_SIGNED_IN:
- this.openFxAManagePageFromFxaMenu(panel);
+ this.openFxAManagePageFromFxaMenu(sourceElement);
}
},
@@ -1273,10 +1367,13 @@ var gSync = {
switchToTabHavingURI(url, true, { replaceQueryString: true });
},
- async openFxAEmailFirstPageFromFxaMenu(panel = undefined, extraParams = {}) {
- this.emitFxaToolbarTelemetry("login", panel);
+ async openFxAEmailFirstPageFromFxaMenu(
+ sourceElement = undefined,
+ extraParams = {}
+ ) {
+ this.emitFxaToolbarTelemetry("login", sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (panel) {
+ if (sourceElement) {
entryPoint = "fxa_toolbar_button";
}
this.openFxAEmailFirstPage(entryPoint, extraParams);
@@ -1287,10 +1384,10 @@ var gSync = {
switchToTabHavingURI(url, true, { replaceQueryString: true });
},
- async openFxAManagePageFromFxaMenu(panel = undefined) {
- this.emitFxaToolbarTelemetry("account_settings", panel);
+ async openFxAManagePageFromFxaMenu(sourceElement = undefined) {
+ this.emitFxaToolbarTelemetry("account_settings", sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
entryPoint = "fxa_app_menu";
}
this.openFxAManagePage(entryPoint);
@@ -1893,9 +1990,9 @@ var gSync = {
}
},
- doSyncFromFxaMenu(panel) {
+ doSyncFromFxaMenu(sourceElement) {
this.doSync();
- this.emitFxaToolbarTelemetry("sync_now", panel);
+ this.emitFxaToolbarTelemetry("sync_now", sourceElement);
},
openPrefs(entryPoint = "syncbutton", origin = undefined) {
@@ -1905,18 +2002,18 @@ var gSync = {
});
},
- openPrefsFromFxaMenu(type, panel) {
- this.emitFxaToolbarTelemetry(type, panel);
+ openPrefsFromFxaMenu(type, sourceElement) {
+ this.emitFxaToolbarTelemetry(type, sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
entryPoint = "fxa_app_menu";
}
this.openPrefs(entryPoint);
},
- openPrefsFromFxaButton(type, panel) {
+ openPrefsFromFxaButton(type, sourceElement) {
let entryPoint = "fxa_toolbar_button_sync";
- this.emitFxaToolbarTelemetry(type, panel);
+ this.emitFxaToolbarTelemetry(type, sourceElement);
this.openPrefs(entryPoint);
},
@@ -2049,27 +2146,24 @@ var gSync = {
// This should only be shown if we have enabled the pxiPanel via
// an experiment or explicitly through prefs
- updateCTAPanel() {
+ updateCTAPanel(anchor) {
const mainPanelEl = PanelMultiView.getViewNode(
document,
"PanelUI-fxa-cta-menu"
);
- const syncCtaEl = PanelMultiView.getViewNode(
- document,
- "PanelUI-fxa-menu-sync-button"
- );
- // If we're not in the experiment then we do not enable this at all
- if (!this.PXI_TOOLBAR_ENABLED) {
+ // If we're not in the experiment or in the app menu (hamburger)
+ // do not show this CTA panel
+ if (
+ !this.FXA_CTA_MENU_ENABLED ||
+ (anchor && anchor.id === "appMenu-fxa-label2")
+ ) {
// If we've previously shown this but got disabled
// we should ensure we hide the panel
mainPanelEl.hidden = true;
return;
}
- // If we're already signed in an syncing, we shouldn't show the sync CTA
- syncCtaEl.hidden = this.isSignedIn;
-
// Monitor checks
let monitorPanelEl = PanelMultiView.getViewNode(
document,
@@ -2112,8 +2206,8 @@ var gSync = {
!monitorEnabled && !relayEnabled && !vpnEnabled;
mainPanelEl.hidden = false;
},
- async openMonitorLink(panel) {
- this.emitFxaToolbarTelemetry("monitor_cta", panel);
+ async openMonitorLink(sourceElement) {
+ this.emitFxaToolbarTelemetry("monitor_cta", sourceElement);
await this.openCtaLink(
FX_MONITOR_OAUTH_CLIENT_ID,
new URL("https://monitor.firefox.com"),
@@ -2121,8 +2215,8 @@ var gSync = {
);
},
- async openRelayLink(panel) {
- this.emitFxaToolbarTelemetry("relay_cta", panel);
+ async openRelayLink(sourceElement) {
+ this.emitFxaToolbarTelemetry("relay_cta", sourceElement);
await this.openCtaLink(
FX_RELAY_OAUTH_CLIENT_ID,
new URL("https://relay.firefox.com"),
@@ -2130,8 +2224,8 @@ var gSync = {
);
},
- async openVPNLink(panel) {
- this.emitFxaToolbarTelemetry("vpn_cta", panel);
+ async openVPNLink(sourceElement) {
+ this.emitFxaToolbarTelemetry("vpn_cta", sourceElement);
await this.openCtaLink(
VPN_OAUTH_CLIENT_ID,
new URL("https://www.mozilla.org/en-US/products/vpn/"),
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 72753da622..5f41ca7781 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -9,6 +9,9 @@ var { XPCOMUtils } = ChromeUtils.importESModule(
var { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
+ChromeUtils.importESModule(
+ "resource://gre/modules/MemoryNotificationDB.sys.mjs"
+);
ChromeUtils.importESModule("resource://gre/modules/NotificationDB.sys.mjs");
// lazy module getters
@@ -35,8 +38,6 @@ ChromeUtils.defineESModuleGetters(this, {
DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs",
E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
ExtensionsUI: "resource:///modules/ExtensionsUI.sys.mjs",
- FirefoxViewNotificationManager:
- "resource:///modules/firefox-view-notification-manager.sys.mjs",
HomePage: "resource:///modules/HomePage.sys.mjs",
isProductURL: "chrome://global/content/shopping/ShoppingProduct.mjs",
LightweightThemeConsumer:
@@ -1503,1117 +1504,6 @@ function _createNullPrincipalFromTabUserContextId(tab = gBrowser.selectedTab) {
});
}
-let _resolveDelayedStartup;
-var delayedStartupPromise = new Promise(resolve => {
- _resolveDelayedStartup = resolve;
-});
-
-var gBrowserInit = {
- delayedStartupFinished: false,
- idleTasksFinishedPromise: null,
- idleTaskPromiseResolve: null,
- domContentLoaded: false,
-
- _tabToAdopt: undefined,
-
- _setupFirstContentWindowPaintPromise() {
- let lastTransactionId = window.windowUtils.lastTransactionId;
- let layerTreeListener = () => {
- if (this.getTabToAdopt()) {
- // Need to wait until we finish adopting the tab, or we might end
- // up focusing the initial browser and then losing focus when it
- // gets swapped out for the tab to adopt.
- return;
- }
- removeEventListener("MozLayerTreeReady", layerTreeListener);
- let listener = e => {
- if (e.transactionId > lastTransactionId) {
- window.removeEventListener("MozAfterPaint", listener);
- this._firstContentWindowPaintDeferred.resolve();
- }
- };
- addEventListener("MozAfterPaint", listener);
- };
- addEventListener("MozLayerTreeReady", layerTreeListener);
- },
-
- getTabToAdopt() {
- if (this._tabToAdopt !== undefined) {
- return this._tabToAdopt;
- }
-
- if (window.arguments && window.XULElement.isInstance(window.arguments[0])) {
- this._tabToAdopt = window.arguments[0];
-
- // Clear the reference of the tab being adopted from the arguments.
- window.arguments[0] = null;
- } else {
- // There was no tab to adopt in the arguments, set _tabToAdopt to null
- // to avoid checking it again.
- this._tabToAdopt = null;
- }
-
- return this._tabToAdopt;
- },
-
- _clearTabToAdopt() {
- this._tabToAdopt = null;
- },
-
- // Used to check if the new window is still adopting an existing tab as its first tab
- // (e.g. from the WebExtensions internals).
- isAdoptingTab() {
- return !!this.getTabToAdopt();
- },
-
- onBeforeInitialXULLayout() {
- this._setupFirstContentWindowPaintPromise();
-
- updateBookmarkToolbarVisibility();
-
- // Set a sane starting width/height for all resolutions on new profiles.
- if (ChromeUtils.shouldResistFingerprinting("RoundWindowSize", null)) {
- // When the fingerprinting resistance is enabled, making sure that we don't
- // have a maximum window to interfere with generating rounded window dimensions.
- document.documentElement.setAttribute("sizemode", "normal");
- } else if (!document.documentElement.hasAttribute("width")) {
- const TARGET_WIDTH = 1280;
- const TARGET_HEIGHT = 1040;
- let width = Math.min(screen.availWidth * 0.9, TARGET_WIDTH);
- let height = Math.min(screen.availHeight * 0.9, TARGET_HEIGHT);
-
- document.documentElement.setAttribute("width", width);
- document.documentElement.setAttribute("height", height);
-
- if (width < TARGET_WIDTH && height < TARGET_HEIGHT) {
- document.documentElement.setAttribute("sizemode", "maximized");
- }
- }
- if (AppConstants.MENUBAR_CAN_AUTOHIDE) {
- const toolbarMenubar = document.getElementById("toolbar-menubar");
- // set a default value
- if (!toolbarMenubar.hasAttribute("autohide")) {
- toolbarMenubar.setAttribute("autohide", true);
- }
- document.l10n.setAttributes(
- toolbarMenubar,
- "toolbar-context-menu-menu-bar-cmd"
- );
- toolbarMenubar.setAttribute("data-l10n-attrs", "toolbarname");
- }
-
- // Run menubar initialization first, to avoid TabsInTitlebar code picking
- // up mutations from it and causing a reflow.
- AutoHideMenubar.init();
- // Update the chromemargin attribute so the window can be sized correctly.
- window.TabBarVisibility.update();
- TabsInTitlebar.init();
-
- new LightweightThemeConsumer(document);
-
- if (
- Services.prefs.getBoolPref(
- "toolkit.legacyUserProfileCustomizations.windowIcon",
- false
- )
- ) {
- document.documentElement.setAttribute("icon", "main-window");
- }
-
- // Call this after we set attributes that might change toolbars' computed
- // text color.
- ToolbarIconColor.init();
- },
-
- onDOMContentLoaded() {
- // This needs setting up before we create the first remote browser.
- window.docShell.treeOwner
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIAppWindow).XULBrowserWindow = window.XULBrowserWindow;
- window.browserDOMWindow = new nsBrowserAccess();
-
- gBrowser = window._gBrowser;
- delete window._gBrowser;
- gBrowser.init();
-
- BrowserWindowTracker.track(window);
-
- FirefoxViewHandler.init();
-
- gNavToolbox.palette = document.getElementById(
- "BrowserToolbarPalette"
- ).content;
- for (let area of CustomizableUI.areas) {
- let type = CustomizableUI.getAreaType(area);
- if (type == CustomizableUI.TYPE_TOOLBAR) {
- let node = document.getElementById(area);
- CustomizableUI.registerToolbarNode(node);
- }
- }
- BrowserSearch.initPlaceHolder();
-
- // Hack to ensure that the various initial pages favicon is loaded
- // instantaneously, to avoid flickering and improve perceived performance.
- this._callWithURIToLoad(uriToLoad => {
- let url;
- try {
- url = Services.io.newURI(uriToLoad);
- } catch (e) {
- return;
- }
- let nonQuery = url.prePath + url.filePath;
- if (nonQuery in gPageIcons) {
- gBrowser.setIcon(gBrowser.selectedTab, gPageIcons[nonQuery]);
- }
- });
-
- updateFxaToolbarMenu(gFxaToolbarEnabled, true);
-
- updatePrintCommands(gPrintEnabled);
-
- gUnifiedExtensions.init();
-
- // Setting the focus will cause a style flush, it's preferable to call anything
- // that will modify the DOM from within this function before this call.
- this._setInitialFocus();
-
- this.domContentLoaded = true;
- },
-
- onLoad() {
- gBrowser.addEventListener("DOMUpdateBlockedPopups", gPopupBlockerObserver);
- gBrowser.addEventListener(
- "TranslationsParent:LanguageState",
- FullPageTranslationsPanel
- );
- gBrowser.addEventListener(
- "TranslationsParent:OfferTranslation",
- FullPageTranslationsPanel
- );
- gBrowser.addTabsProgressListener(FullPageTranslationsPanel);
-
- window.addEventListener("AppCommand", HandleAppCommandEvent, true);
-
- // These routines add message listeners. They must run before
- // loading the frame script to ensure that we don't miss any
- // message sent between when the frame script is loaded and when
- // the listener is registered.
- CaptivePortalWatcher.init();
- ZoomUI.init(window);
-
- if (!gMultiProcessBrowser) {
- // There is a Content:Click message manually sent from content.
- gBrowser.tabpanels.addEventListener("click", contentAreaClick, {
- capture: true,
- mozSystemGroup: true,
- });
- }
-
- // hook up UI through progress listener
- gBrowser.addProgressListener(window.XULBrowserWindow);
- gBrowser.addTabsProgressListener(window.TabsProgressListener);
-
- SidebarUI.init();
-
- // We do this in onload because we want to ensure the button's state
- // doesn't flicker as the window is being shown.
- DownloadsButton.init();
-
- // Certain kinds of automigration rely on this notification to complete
- // their tasks BEFORE the browser window is shown. SessionStore uses it to
- // restore tabs into windows AFTER important parts like gMultiProcessBrowser
- // have been initialized.
- Services.obs.notifyObservers(window, "browser-window-before-show");
-
- if (!window.toolbar.visible) {
- // adjust browser UI for popups
- gURLBar.readOnly = true;
- }
-
- // Misc. inits.
- gUIDensity.init();
- TabletModeUpdater.init();
- CombinedStopReload.ensureInitialized();
- gPrivateBrowsingUI.init();
- BrowserSearch.init();
- BrowserPageActions.init();
- if (gToolbarKeyNavEnabled) {
- ToolbarKeyboardNavigator.init();
- }
-
- // Update UI if browser is under remote control.
- gRemoteControl.updateVisualCue();
-
- // If we are given a tab to swap in, take care of it before first paint to
- // avoid an about:blank flash.
- let tabToAdopt = this.getTabToAdopt();
- if (tabToAdopt) {
- let evt = new CustomEvent("before-initial-tab-adopted", {
- bubbles: true,
- });
- gBrowser.tabpanels.dispatchEvent(evt);
-
- // Stop the about:blank load
- gBrowser.stop();
-
- // Remove the speculative focus from the urlbar to let the url be formatted.
- gURLBar.removeAttribute("focused");
-
- let swapBrowsers = () => {
- try {
- gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToAdopt);
- } catch (e) {
- console.error(e);
- }
-
- // Clear the reference to the tab once its adoption has been completed.
- this._clearTabToAdopt();
- };
- if (tabToAdopt.linkedBrowser.isRemoteBrowser) {
- // For remote browsers, wait for the paint event, otherwise the tabs
- // are not yet ready and focus gets confused because the browser swaps
- // out while tabs are switching.
- addEventListener("MozAfterPaint", swapBrowsers, { once: true });
- } else {
- swapBrowsers();
- }
- }
-
- // Wait until chrome is painted before executing code not critical to making the window visible
- this._boundDelayedStartup = this._delayedStartup.bind(this);
- window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
-
- if (!PrivateBrowsingUtils.enabled) {
- document.getElementById("Tools:PrivateBrowsing").hidden = true;
- // Setting disabled doesn't disable the shortcut, so we just remove
- // the keybinding.
- document.getElementById("key_privatebrowsing").remove();
- }
-
- if (BrowserUIUtils.quitShortcutDisabled) {
- document.getElementById("key_quitApplication").remove();
- document.getElementById("menu_FileQuitItem").removeAttribute("key");
-
- PanelMultiView.getViewNode(
- document,
- "appMenu-quit-button2"
- )?.removeAttribute("key");
- }
-
- this._loadHandled = true;
- },
-
- _cancelDelayedStartup() {
- window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
- this._boundDelayedStartup = null;
- },
-
- _delayedStartup() {
- let { TelemetryTimestamps } = ChromeUtils.importESModule(
- "resource://gre/modules/TelemetryTimestamps.sys.mjs"
- );
- TelemetryTimestamps.add("delayedStartupStarted");
-
- this._cancelDelayedStartup();
-
- // Bug 1531854 - The hidden window is force-created here
- // until all of its dependencies are handled.
- Services.appShell.hiddenDOMWindow;
-
- gBrowser.addEventListener(
- "PermissionStateChange",
- function () {
- gIdentityHandler.refreshIdentityBlock();
- gPermissionPanel.updateSharingIndicator();
- },
- true
- );
-
- this._handleURIToLoad();
-
- Services.obs.addObserver(gIdentityHandler, "perm-changed");
- Services.obs.addObserver(gRemoteControl, "devtools-socket");
- Services.obs.addObserver(gRemoteControl, "marionette-listening");
- Services.obs.addObserver(gRemoteControl, "remote-listening");
- Services.obs.addObserver(
- gSessionHistoryObserver,
- "browser:purge-session-history"
- );
- Services.obs.addObserver(
- gStoragePressureObserver,
- "QuotaManager::StoragePressure"
- );
- Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled");
- Services.obs.addObserver(gXPInstallObserver, "addon-install-started");
- Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked");
- Services.obs.addObserver(
- gXPInstallObserver,
- "addon-install-fullscreen-blocked"
- );
- Services.obs.addObserver(
- gXPInstallObserver,
- "addon-install-origin-blocked"
- );
- Services.obs.addObserver(
- gXPInstallObserver,
- "addon-install-policy-blocked"
- );
- Services.obs.addObserver(
- gXPInstallObserver,
- "addon-install-webapi-blocked"
- );
- Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
- Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation");
- Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
-
- BrowserOffline.init();
- CanvasPermissionPromptHelper.init();
- WebAuthnPromptHelper.init();
- ContentAnalysis.initialize();
-
- // Initialize the full zoom setting.
- // We do this before the session restore service gets initialized so we can
- // apply full zoom settings to tabs restored by the session restore service.
- FullZoom.init();
- PanelUI.init(shouldSuppressPopupNotifications);
- ReportBrokenSite.init(gBrowser);
-
- UpdateUrlbarSearchSplitterState();
-
- BookmarkingUI.init();
- BrowserSearch.delayedStartupInit();
- SearchUIUtils.init();
- gProtectionsHandler.init();
- HomePage.delayedStartup().catch(console.error);
-
- let safeMode = document.getElementById("helpSafeMode");
- if (Services.appinfo.inSafeMode) {
- document.l10n.setAttributes(safeMode, "menu-help-exit-troubleshoot-mode");
- safeMode.setAttribute(
- "appmenu-data-l10n-id",
- "appmenu-help-exit-troubleshoot-mode"
- );
- }
-
- // BiDi UI
- gBidiUI = isBidiEnabled();
- if (gBidiUI) {
- document.getElementById("documentDirection-separator").hidden = false;
- document.getElementById("documentDirection-swap").hidden = false;
- document.getElementById("textfieldDirection-separator").hidden = false;
- document.getElementById("textfieldDirection-swap").hidden = false;
- }
-
- // Setup click-and-hold gestures access to the session history
- // menus if global click-and-hold isn't turned on
- if (!Services.prefs.getBoolPref("ui.click_hold_context_menus", false)) {
- SetClickAndHoldHandlers();
- }
-
- function initBackForwardButtonTooltip(tooltipId, l10nId, shortcutId) {
- let shortcut = document.getElementById(shortcutId);
- shortcut = ShortcutUtils.prettifyShortcut(shortcut);
-
- let tooltip = document.getElementById(tooltipId);
- document.l10n.setAttributes(tooltip, l10nId, { shortcut });
- }
-
- initBackForwardButtonTooltip(
- "back-button-tooltip-description",
- "navbar-tooltip-back-2",
- "goBackKb"
- );
-
- initBackForwardButtonTooltip(
- "forward-button-tooltip-description",
- "navbar-tooltip-forward-2",
- "goForwardKb"
- );
-
- PlacesToolbarHelper.init();
-
- ctrlTab.readPref();
- Services.prefs.addObserver(ctrlTab.prefName, ctrlTab);
-
- // The object handling the downloads indicator is initialized here in the
- // delayed startup function, but the actual indicator element is not loaded
- // unless there are downloads to be displayed.
- DownloadsButton.initializeIndicator();
-
- if (AppConstants.platform != "macosx") {
- updateEditUIVisibility();
- let placesContext = document.getElementById("placesContext");
- placesContext.addEventListener("popupshowing", updateEditUIVisibility);
- placesContext.addEventListener("popuphiding", updateEditUIVisibility);
- }
-
- FullScreen.init();
- MenuTouchModeObserver.init();
-
- if (AppConstants.MOZ_DATA_REPORTING) {
- gDataNotificationInfoBar.init();
- }
-
- if (!AppConstants.MOZILLA_OFFICIAL) {
- DevelopmentHelpers.init();
- }
-
- gExtensionsNotifications.init();
-
- let wasMinimized = window.windowState == window.STATE_MINIMIZED;
- window.addEventListener("sizemodechange", () => {
- let isMinimized = window.windowState == window.STATE_MINIMIZED;
- if (wasMinimized != isMinimized) {
- wasMinimized = isMinimized;
- UpdatePopupNotificationsVisibility();
- }
- });
-
- window.addEventListener("mousemove", MousePosTracker);
- window.addEventListener("dragover", MousePosTracker);
-
- gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
- gNavToolbox.addEventListener("aftercustomization", CustomizationHandler);
-
- SessionStore.promiseInitialized.then(() => {
- // Bail out if the window has been closed in the meantime.
- if (window.closed) {
- return;
- }
-
- // Enable the Restore Last Session command if needed
- RestoreLastSessionObserver.init();
-
- SidebarUI.startDelayedLoad();
-
- PanicButtonNotifier.init();
- });
-
- if (BrowserHandler.kiosk) {
- // We don't modify popup windows for kiosk mode
- if (!gURLBar.readOnly) {
- window.fullScreen = true;
- }
- }
-
- if (Services.policies.status === Services.policies.ACTIVE) {
- if (!Services.policies.isAllowed("hideShowMenuBar")) {
- document
- .getElementById("toolbar-menubar")
- .removeAttribute("toolbarname");
- }
- if (!Services.policies.isAllowed("filepickers")) {
- let savePageCommand = document.getElementById("Browser:SavePage");
- let openFileCommand = document.getElementById("Browser:OpenFile");
-
- savePageCommand.setAttribute("disabled", "true");
- openFileCommand.setAttribute("disabled", "true");
-
- document.addEventListener("FilePickerBlocked", function (event) {
- let browser = event.target;
-
- let notificationBox = browser
- .getTabBrowser()
- ?.getNotificationBox(browser);
-
- // Prevent duplicate notifications
- if (
- notificationBox &&
- !notificationBox.getNotificationWithValue("filepicker-blocked")
- ) {
- notificationBox.appendNotification("filepicker-blocked", {
- label: {
- "l10n-id": "filepicker-blocked-infobar",
- },
- priority: notificationBox.PRIORITY_INFO_LOW,
- });
- }
- });
- }
- let policies = Services.policies.getActivePolicies();
- if ("ManagedBookmarks" in policies) {
- let managedBookmarks = policies.ManagedBookmarks;
- let children = managedBookmarks.filter(
- child => !("toplevel_name" in child)
- );
- if (children.length) {
- let managedBookmarksButton =
- document.createXULElement("toolbarbutton");
- managedBookmarksButton.setAttribute("id", "managed-bookmarks");
- managedBookmarksButton.setAttribute("class", "bookmark-item");
- let toplevel = managedBookmarks.find(
- element => "toplevel_name" in element
- );
- if (toplevel) {
- managedBookmarksButton.setAttribute(
- "label",
- toplevel.toplevel_name
- );
- } else {
- document.l10n.setAttributes(
- managedBookmarksButton,
- "managed-bookmarks"
- );
- }
- managedBookmarksButton.setAttribute("context", "placesContext");
- managedBookmarksButton.setAttribute("container", "true");
- managedBookmarksButton.setAttribute("removable", "false");
- managedBookmarksButton.setAttribute("type", "menu");
-
- let managedBookmarksPopup = document.createXULElement("menupopup");
- managedBookmarksPopup.setAttribute("id", "managed-bookmarks-popup");
- managedBookmarksPopup.setAttribute(
- "oncommand",
- "PlacesToolbarHelper.openManagedBookmark(event);"
- );
- managedBookmarksPopup.setAttribute(
- "ondragover",
- "event.dataTransfer.effectAllowed='none';"
- );
- managedBookmarksPopup.setAttribute(
- "ondragstart",
- "PlacesToolbarHelper.onDragStartManaged(event);"
- );
- managedBookmarksPopup.setAttribute(
- "onpopupshowing",
- "PlacesToolbarHelper.populateManagedBookmarks(this);"
- );
- managedBookmarksPopup.setAttribute("placespopup", "true");
- managedBookmarksPopup.setAttribute("is", "places-popup");
- managedBookmarksPopup.classList.add("toolbar-menupopup");
- managedBookmarksButton.appendChild(managedBookmarksPopup);
-
- gNavToolbox.palette.appendChild(managedBookmarksButton);
-
- CustomizableUI.ensureWidgetPlacedInWindow(
- "managed-bookmarks",
- window
- );
-
- // Add button if it doesn't exist
- if (!CustomizableUI.getPlacementOfWidget("managed-bookmarks")) {
- CustomizableUI.addWidgetToArea(
- "managed-bookmarks",
- CustomizableUI.AREA_BOOKMARKS,
- 0
- );
- }
- }
- }
- }
-
- CaptivePortalWatcher.delayedStartup();
-
- ShoppingSidebarManager.ensureInitialized();
-
- SessionStore.promiseAllWindowsRestored.then(() => {
- this._schedulePerWindowIdleTasks();
- document.documentElement.setAttribute("sessionrestored", "true");
- });
-
- this.delayedStartupFinished = true;
- _resolveDelayedStartup();
- Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
- TelemetryTimestamps.add("delayedStartupFinished");
- // We've announced that delayed startup has finished. Do not add code past this point.
- },
-
- /**
- * Resolved on the first MozLayerTreeReady and next MozAfterPaint in the
- * parent process.
- */
- get firstContentWindowPaintPromise() {
- return this._firstContentWindowPaintDeferred.promise;
- },
-
- _setInitialFocus() {
- let initiallyFocusedElement = document.commandDispatcher.focusedElement;
-
- // To prevent startup flicker, the urlbar has the 'focused' attribute set
- // by default. If we are not sure the urlbar will be focused in this
- // window, we need to remove the attribute before first paint.
- // TODO (bug 1629956): The urlbar having the 'focused' attribute by default
- // isn't a useful optimization anymore since UrlbarInput needs layout
- // information to focus the urlbar properly.
- let shouldRemoveFocusedAttribute = true;
-
- this._callWithURIToLoad(uriToLoad => {
- if (
- isBlankPageURL(uriToLoad) ||
- uriToLoad == "about:privatebrowsing" ||
- this.getTabToAdopt()?.isEmpty
- ) {
- gURLBar.select();
- shouldRemoveFocusedAttribute = false;
- return;
- }
-
- // If the initial browser is remote, in order to optimize for first paint,
- // we'll defer switching focus to that browser until it has painted.
- // Otherwise use a regular promise to guarantee that mutationobserver
- // microtasks that could affect focusability have run.
- let promise = gBrowser.selectedBrowser.isRemoteBrowser
- ? this.firstContentWindowPaintPromise
- : Promise.resolve();
-
- promise.then(() => {
- // If focus didn't move while we were waiting, we're okay to move to
- // the browser.
- if (
- document.commandDispatcher.focusedElement == initiallyFocusedElement
- ) {
- gBrowser.selectedBrowser.focus();
- }
- });
- });
-
- // Delay removing the attribute using requestAnimationFrame to avoid
- // invalidating styles multiple times in a row if uriToLoadPromise
- // resolves before first paint.
- if (shouldRemoveFocusedAttribute) {
- window.requestAnimationFrame(() => {
- if (shouldRemoveFocusedAttribute) {
- gURLBar.removeAttribute("focused");
- }
- });
- }
- },
-
- _handleURIToLoad() {
- this._callWithURIToLoad(uriToLoad => {
- if (!uriToLoad) {
- // We don't check whether window.arguments[5] (userContextId) is set
- // because tabbrowser.js takes care of that for the initial tab.
- return;
- }
-
- // We don't check if uriToLoad is a XULElement because this case has
- // already been handled before first paint, and the argument cleared.
- if (Array.isArray(uriToLoad)) {
- // This function throws for certain malformed URIs, so use exception handling
- // so that we don't disrupt startup
- try {
- gBrowser.loadTabs(uriToLoad, {
- inBackground: false,
- replace: true,
- // See below for the semantics of window.arguments. Only the minimum is supported.
- userContextId: window.arguments[5],
- triggeringPrincipal:
- window.arguments[8] ||
- Services.scriptSecurityManager.getSystemPrincipal(),
- allowInheritPrincipal: window.arguments[9],
- csp: window.arguments[10],
- fromExternal: true,
- });
- } catch (e) {}
- } else if (window.arguments.length >= 3) {
- // window.arguments[1]: extraOptions (nsIPropertyBag)
- // [2]: referrerInfo (nsIReferrerInfo)
- // [3]: postData (nsIInputStream)
- // [4]: allowThirdPartyFixup (bool)
- // [5]: userContextId (int)
- // [6]: originPrincipal (nsIPrincipal)
- // [7]: originStoragePrincipal (nsIPrincipal)
- // [8]: triggeringPrincipal (nsIPrincipal)
- // [9]: allowInheritPrincipal (bool)
- // [10]: csp (nsIContentSecurityPolicy)
- // [11]: nsOpenWindowInfo
- let userContextId =
- window.arguments[5] != undefined
- ? window.arguments[5]
- : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
-
- let hasValidUserGestureActivation = undefined;
- let fromExternal = undefined;
- let globalHistoryOptions = undefined;
- let triggeringRemoteType = undefined;
- let forceAllowDataURI = false;
- let wasSchemelessInput = false;
- if (window.arguments[1]) {
- if (!(window.arguments[1] instanceof Ci.nsIPropertyBag2)) {
- throw new Error(
- "window.arguments[1] must be null or Ci.nsIPropertyBag2!"
- );
- }
-
- let extraOptions = window.arguments[1];
- if (extraOptions.hasKey("hasValidUserGestureActivation")) {
- hasValidUserGestureActivation = extraOptions.getPropertyAsBool(
- "hasValidUserGestureActivation"
- );
- }
- if (extraOptions.hasKey("fromExternal")) {
- fromExternal = extraOptions.getPropertyAsBool("fromExternal");
- }
- if (extraOptions.hasKey("triggeringSponsoredURL")) {
- globalHistoryOptions = {
- triggeringSponsoredURL: extraOptions.getPropertyAsACString(
- "triggeringSponsoredURL"
- ),
- };
- if (extraOptions.hasKey("triggeringSponsoredURLVisitTimeMS")) {
- globalHistoryOptions.triggeringSponsoredURLVisitTimeMS =
- extraOptions.getPropertyAsUint64(
- "triggeringSponsoredURLVisitTimeMS"
- );
- }
- }
- if (extraOptions.hasKey("triggeringRemoteType")) {
- triggeringRemoteType = extraOptions.getPropertyAsACString(
- "triggeringRemoteType"
- );
- }
- if (extraOptions.hasKey("forceAllowDataURI")) {
- forceAllowDataURI =
- extraOptions.getPropertyAsBool("forceAllowDataURI");
- }
- if (extraOptions.hasKey("wasSchemelessInput")) {
- wasSchemelessInput =
- extraOptions.getPropertyAsBool("wasSchemelessInput");
- }
- }
-
- try {
- openLinkIn(uriToLoad, "current", {
- referrerInfo: window.arguments[2] || null,
- postData: window.arguments[3] || null,
- allowThirdPartyFixup: window.arguments[4] || false,
- userContextId,
- // pass the origin principal (if any) and force its use to create
- // an initial about:blank viewer if present:
- originPrincipal: window.arguments[6],
- originStoragePrincipal: window.arguments[7],
- triggeringPrincipal: window.arguments[8],
- // TODO fix allowInheritPrincipal to default to false.
- // Default to true unless explicitly set to false because of bug 1475201.
- allowInheritPrincipal: window.arguments[9] !== false,
- csp: window.arguments[10],
- forceAboutBlankViewerInCurrent: !!window.arguments[6],
- forceAllowDataURI,
- hasValidUserGestureActivation,
- fromExternal,
- globalHistoryOptions,
- triggeringRemoteType,
- wasSchemelessInput,
- });
- } catch (e) {
- console.error(e);
- }
-
- window.focus();
- } else {
- // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
- // Such callers expect that window.arguments[0] is handled as a single URI.
- loadOneOrMoreURIs(
- uriToLoad,
- Services.scriptSecurityManager.getSystemPrincipal(),
- null
- );
- }
- });
- },
-
- /**
- * Use this function as an entry point to schedule tasks that
- * need to run once per window after startup, and can be scheduled
- * by using an idle callback.
- *
- * The functions scheduled here will fire from idle callbacks
- * once every window has finished being restored by session
- * restore, and after the equivalent only-once tasks
- * have run (from _scheduleStartupIdleTasks in BrowserGlue.sys.mjs).
- */
- _schedulePerWindowIdleTasks() {
- // Bail out if the window has been closed in the meantime.
- if (window.closed) {
- return;
- }
-
- function scheduleIdleTask(func, options) {
- requestIdleCallback(function idleTaskRunner() {
- if (!window.closed) {
- func();
- }
- }, options);
- }
-
- scheduleIdleTask(() => {
- // Initialize the Sync UI
- gSync.init();
- });
-
- scheduleIdleTask(() => {
- // Read prefers-reduced-motion setting
- let reduceMotionQuery = window.matchMedia(
- "(prefers-reduced-motion: reduce)"
- );
- function readSetting() {
- gReduceMotionSetting = reduceMotionQuery.matches;
- }
- reduceMotionQuery.addListener(readSetting);
- readSetting();
- });
-
- scheduleIdleTask(() => {
- // setup simple gestures support
- gGestureSupport.init(true);
-
- // setup history swipe animation
- gHistorySwipeAnimation.init();
- });
-
- scheduleIdleTask(() => {
- gBrowserThumbnails.init();
- });
-
- scheduleIdleTask(
- () => {
- // Initialize the download manager some time after the app starts so that
- // auto-resume downloads begin (such as after crashing or quitting with
- // active downloads) and speeds up the first-load of the download manager UI.
- // If the user manually opens the download manager before the timeout, the
- // downloads will start right away, and initializing again won't hurt.
- try {
- DownloadsCommon.initializeAllDataLinks();
- ChromeUtils.importESModule(
- "resource:///modules/DownloadsTaskbar.sys.mjs"
- ).DownloadsTaskbar.registerIndicator(window);
- if (AppConstants.platform == "macosx") {
- ChromeUtils.importESModule(
- "resource:///modules/DownloadsMacFinderProgress.sys.mjs"
- ).DownloadsMacFinderProgress.register();
- }
- Services.telemetry.setEventRecordingEnabled("downloads", true);
- } catch (ex) {
- console.error(ex);
- }
- },
- { timeout: 10000 }
- );
-
- if (Win7Features) {
- scheduleIdleTask(() => Win7Features.onOpenWindow());
- }
-
- scheduleIdleTask(async () => {
- NewTabPagePreloading.maybeCreatePreloadedBrowser(window);
- });
-
- scheduleIdleTask(() => {
- gGfxUtils.init();
- });
-
- // This should always go last, since the idle tasks (except for the ones with
- // timeouts) should execute in order. Note that this observer notification is
- // not guaranteed to fire, since the window could close before we get here.
- scheduleIdleTask(() => {
- this.idleTaskPromiseResolve();
- Services.obs.notifyObservers(
- window,
- "browser-idle-startup-tasks-finished"
- );
- });
-
- scheduleIdleTask(() => {
- gProfiles.init();
- });
- },
-
- // Returns the URI(s) to load at startup if it is immediately known, or a
- // promise resolving to the URI to load.
- get uriToLoadPromise() {
- delete this.uriToLoadPromise;
- return (this.uriToLoadPromise = (function () {
- // window.arguments[0]: URI to load (string), or an nsIArray of
- // nsISupportsStrings to load, or a xul:tab of
- // a tabbrowser, which will be replaced by this
- // window (for this case, all other arguments are
- // ignored).
- let uri = window.arguments?.[0];
- if (!uri || window.XULElement.isInstance(uri)) {
- return null;
- }
-
- let defaultArgs = BrowserHandler.defaultArgs;
-
- // If the given URI is different from the homepage, we want to load it.
- if (uri != defaultArgs) {
- AboutNewTab.noteNonDefaultStartup();
-
- if (uri instanceof Ci.nsIArray) {
- // Transform the nsIArray of nsISupportsString's into a JS Array of
- // JS strings.
- return Array.from(
- uri.enumerate(Ci.nsISupportsString),
- supportStr => supportStr.data
- );
- } else if (uri instanceof Ci.nsISupportsString) {
- return uri.data;
- }
- return uri;
- }
-
- // The URI appears to be the the homepage. We want to load it only if
- // session restore isn't about to override the homepage.
- let willOverride = SessionStartup.willOverrideHomepage;
- if (typeof willOverride == "boolean") {
- return willOverride ? null : uri;
- }
- return willOverride.then(willOverrideHomepage =>
- willOverrideHomepage ? null : uri
- );
- })());
- },
-
- // Calls the given callback with the URI to load at startup.
- // Synchronously if possible, or after uriToLoadPromise resolves otherwise.
- _callWithURIToLoad(callback) {
- let uriToLoad = this.uriToLoadPromise;
- if (uriToLoad && uriToLoad.then) {
- uriToLoad.then(callback);
- } else {
- callback(uriToLoad);
- }
- },
-
- onUnload() {
- gUIDensity.uninit();
-
- TabsInTitlebar.uninit();
-
- ToolbarIconColor.uninit();
-
- // In certain scenarios it's possible for unload to be fired before onload,
- // (e.g. if the window is being closed after browser.js loads but before the
- // load completes). In that case, there's nothing to do here.
- if (!this._loadHandled) {
- return;
- }
-
- // First clean up services initialized in gBrowserInit.onLoad (or those whose
- // uninit methods don't depend on the services having been initialized).
-
- CombinedStopReload.uninit();
-
- gGestureSupport.init(false);
-
- gHistorySwipeAnimation.uninit();
-
- FullScreen.uninit();
-
- gSync.uninit();
-
- gExtensionsNotifications.uninit();
- gUnifiedExtensions.uninit();
-
- try {
- gBrowser.removeProgressListener(window.XULBrowserWindow);
- gBrowser.removeTabsProgressListener(window.TabsProgressListener);
- } catch (ex) {}
-
- PlacesToolbarHelper.uninit();
-
- BookmarkingUI.uninit();
-
- TabletModeUpdater.uninit();
-
- gTabletModePageCounter.finish();
-
- CaptivePortalWatcher.uninit();
-
- SidebarUI.uninit();
-
- DownloadsButton.uninit();
-
- if (gToolbarKeyNavEnabled) {
- ToolbarKeyboardNavigator.uninit();
- }
-
- BrowserSearch.uninit();
-
- NewTabPagePreloading.removePreloadedBrowser(window);
-
- FirefoxViewHandler.uninit();
-
- // Now either cancel delayedStartup, or clean up the services initialized from
- // it.
- if (this._boundDelayedStartup) {
- this._cancelDelayedStartup();
- } else {
- if (Win7Features) {
- Win7Features.onCloseWindow();
- }
- Services.prefs.removeObserver(ctrlTab.prefName, ctrlTab);
- ctrlTab.uninit();
- gBrowserThumbnails.uninit();
- gProtectionsHandler.uninit();
- FullZoom.destroy();
-
- Services.obs.removeObserver(gIdentityHandler, "perm-changed");
- Services.obs.removeObserver(gRemoteControl, "devtools-socket");
- Services.obs.removeObserver(gRemoteControl, "marionette-listening");
- Services.obs.removeObserver(gRemoteControl, "remote-listening");
- Services.obs.removeObserver(
- gSessionHistoryObserver,
- "browser:purge-session-history"
- );
- Services.obs.removeObserver(
- gStoragePressureObserver,
- "QuotaManager::StoragePressure"
- );
- Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
- Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
- Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
- Services.obs.removeObserver(
- gXPInstallObserver,
- "addon-install-fullscreen-blocked"
- );
- Services.obs.removeObserver(
- gXPInstallObserver,
- "addon-install-origin-blocked"
- );
- Services.obs.removeObserver(
- gXPInstallObserver,
- "addon-install-policy-blocked"
- );
- Services.obs.removeObserver(
- gXPInstallObserver,
- "addon-install-webapi-blocked"
- );
- Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
- Services.obs.removeObserver(
- gXPInstallObserver,
- "addon-install-confirmation"
- );
- Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
-
- MenuTouchModeObserver.uninit();
- BrowserOffline.uninit();
- CanvasPermissionPromptHelper.uninit();
- WebAuthnPromptHelper.uninit();
- PanelUI.uninit();
- }
-
- // Final window teardown, do this last.
- gBrowser.destroy();
- window.XULBrowserWindow = null;
- window.docShell.treeOwner
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIAppWindow).XULBrowserWindow = null;
- window.browserDOMWindow = null;
- },
-};
-
-ChromeUtils.defineLazyGetter(
- gBrowserInit,
- "_firstContentWindowPaintDeferred",
- () => Promise.withResolvers()
-);
-
-gBrowserInit.idleTasksFinishedPromise = new Promise(resolve => {
- gBrowserInit.idleTaskPromiseResolve = resolve;
-});
-
function HandleAppCommandEvent(evt) {
switch (evt.command) {
case "Back":
@@ -2634,7 +1524,7 @@ function HandleAppCommandEvent(evt) {
BrowserSearch.webSearch();
break;
case "Bookmarks":
- SidebarUI.toggle("viewBookmarksSidebar");
+ SidebarController.toggle("viewBookmarksSidebar");
break;
case "Home":
BrowserCommands.home();
@@ -3636,7 +2526,7 @@ const BrowserSearch = {
event
) {
event = getRootEvent(event);
- let where = whereToOpenLink(event);
+ let where = BrowserUtils.whereToOpenLink(event);
if (where == "current") {
// override: historically search opens in new tab
where = "tab";
@@ -3942,14 +2832,6 @@ function FillHistoryMenu(aParent) {
return true;
}
-function BrowserDownloadsUI() {
- if (PrivateBrowsingUtils.isWindowPrivate(window)) {
- openTrustedLinkIn("about:downloads", "tab");
- } else {
- PlacesCommandHook.showPlacesOrganizer("Downloads");
- }
-}
-
function toOpenWindowByType(inType, uri, features) {
var topWindow = Services.wm.getMostRecentWindow(inType);
@@ -4850,7 +3732,7 @@ var XULBrowserWindow = {
}
}
- if (TranslationsParent.isRestrictedPage(gBrowser)) {
+ if (TranslationsParent.isFullPageTranslationsRestrictedForPage(gBrowser)) {
this._menuItemForTranslations.setAttribute("disabled", "true");
} else {
this._menuItemForTranslations.removeAttribute("disabled");
@@ -5658,7 +4540,7 @@ nsBrowserAccess.prototype = {
: PrivateBrowsingUtils.isWindowPrivate(window);
switch (aWhere) {
- case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW:
+ case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW: {
// FIXME: Bug 408379. So how come this doesn't send the
// referrer like the other loads do?
var url = aURI && aURI.spec;
@@ -5702,6 +4584,7 @@ nsBrowserAccess.prototype = {
console.error(ex);
}
break;
+ }
case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB:
case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB_BACKGROUND: {
// If we have an opener, that means that the caller is expecting access
@@ -6112,7 +4995,7 @@ function setToolbarVisibility(
document.documentElement.toggleAttribute(overlapAttr, false);
break;
case "newtab":
- default:
+ default: {
let currentURI = gBrowser?.currentURI;
if (!gBrowserInit.domContentLoaded) {
let uriToLoad = gBrowserInit.uriToLoadPromise;
@@ -6129,6 +5012,7 @@ function setToolbarVisibility(
isVisible = BookmarkingUI.isOnNewTabPage(currentURI);
document.documentElement.toggleAttribute(overlapAttr, isVisible);
break;
+ }
}
}
@@ -6285,9 +5169,10 @@ var gUIDensity = {
}
let docs = [document.documentElement];
- let shouldUpdateSidebar = SidebarUI.initialized && SidebarUI.isOpen;
+ let shouldUpdateSidebar =
+ SidebarController.initialized && SidebarController.isOpen;
if (shouldUpdateSidebar) {
- docs.push(SidebarUI.browser.contentDocument.documentElement);
+ docs.push(SidebarController.browser.contentDocument.documentElement);
}
for (let doc of docs) {
switch (mode) {
@@ -6303,7 +5188,7 @@ var gUIDensity = {
}
}
if (shouldUpdateSidebar) {
- let tree = SidebarUI.browser.contentDocument.querySelector(
+ let tree = SidebarController.browser.contentDocument.querySelector(
".sidebar-placesTree"
);
if (tree) {
@@ -6540,7 +5425,7 @@ function handleLinkClick(event, href, linkNode) {
return false;
}
- var where = whereToOpenLink(event);
+ var where = BrowserUtils.whereToOpenLink(event);
if (where == "current") {
return false;
}
@@ -6613,7 +5498,7 @@ function middleMousePaste(event) {
// if it's not the current tab, we don't need to do anything because the
// browser doesn't exist.
- let where = whereToOpenLink(event, true, false);
+ let where = BrowserUtils.whereToOpenLink(event, true, false);
let lastLocationChange;
if (where == "current") {
lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
@@ -6727,13 +5612,6 @@ function handleDroppedLink(
}
}
-function BrowserForceEncodingDetection() {
- gBrowser.selectedBrowser.forceEncodingDetection();
- BrowserCommands.reloadWithFlags(
- Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE
- );
-}
-
var ToolbarContextMenu = {
updateDownloadsAutoHide(popup) {
let checkbox = document.getElementById(
@@ -7628,84 +6506,6 @@ var MailIntegration = {
},
};
-/**
- * Open about:addons page by given view id.
- * @param {String} aView
- * View id of page that will open.
- * e.g. "addons://discover/"
- * @param {Object} options
- * {
- * selectTabByViewId: If true, if there is the tab opening page having
- * same view id, select the tab. Else if the current
- * page is blank, load on it. Otherwise, open a new
- * tab, then load on it.
- * If false, if there is the tab opening
- * about:addoons page, select the tab and load page
- * for view id on it. Otherwise, leave the loading
- * behavior to switchToTabHavingURI().
- * If no options, handles as false.
- * }
- * @returns {Promise} When the Promise resolves, returns window object loaded the
- * view id.
- */
-function BrowserOpenAddonsMgr(aView, { selectTabByViewId = false } = {}) {
- return new Promise(resolve => {
- let emWindow;
- let browserWindow;
-
- var receivePong = function (aSubject) {
- let browserWin = aSubject.browsingContext.topChromeWindow;
- if (!emWindow || browserWin == window /* favor the current window */) {
- if (
- selectTabByViewId &&
- aSubject.gViewController.currentViewId !== aView
- ) {
- return;
- }
-
- emWindow = aSubject;
- browserWindow = browserWin;
- }
- };
- Services.obs.addObserver(receivePong, "EM-pong");
- Services.obs.notifyObservers(null, "EM-ping");
- Services.obs.removeObserver(receivePong, "EM-pong");
-
- if (emWindow) {
- if (aView && !selectTabByViewId) {
- emWindow.loadView(aView);
- }
- let tab = browserWindow.gBrowser.getTabForBrowser(
- emWindow.docShell.chromeEventHandler
- );
- browserWindow.gBrowser.selectedTab = tab;
- emWindow.focus();
- resolve(emWindow);
- return;
- }
-
- if (selectTabByViewId) {
- const target = isBlankPageURL(gBrowser.currentURI.spec)
- ? "current"
- : "tab";
- openTrustedLinkIn("about:addons", target);
- } else {
- // This must be a new load, else the ping/pong would have
- // found the window above.
- switchToTabHavingURI("about:addons", true);
- }
-
- Services.obs.addObserver(function observer(aSubject, aTopic) {
- Services.obs.removeObserver(observer, aTopic);
- if (aView) {
- aSubject.loadView(aView);
- }
- aSubject.focus();
- resolve(aSubject);
- }, "EM-loaded");
- });
-}
-
function AddKeywordForSearchField() {
if (!gContextMenu) {
throw new Error("Context menu doesn't seem to be open.");
@@ -8256,7 +7056,7 @@ function safeModeRestart() {
*/
function duplicateTabIn(aTab, where, delta) {
switch (where) {
- case "window":
+ case "window": {
let otherWin = OpenBrowserWindow({
private: PrivateBrowsingUtils.isBrowserPrivate(aTab.linkedBrowser),
});
@@ -8278,6 +7078,7 @@ function duplicateTabIn(aTab, where, delta) {
"browser-delayed-startup-finished"
);
break;
+ }
case "tabshifted":
SessionStore.duplicateTab(window, aTab, delta);
// A background tab has been opened, nothing else to do here.
@@ -9125,15 +7926,14 @@ var ConfirmationHint = {
* - event (DOM event): The event that triggered the feedback
* - descriptionId (string): message ID of the description text
* - position (string): position of the panel relative to the anchor.
- *
+ * - l10nArgs (object): l10n arguments for the messageId.
*/
show(anchor, messageId, options = {}) {
this._reset();
MozXULElement.insertFTLIfNeeded("toolkit/branding/brandings.ftl");
MozXULElement.insertFTLIfNeeded("browser/confirmationHints.ftl");
- document.l10n.setAttributes(this._message, messageId);
-
+ document.l10n.setAttributes(this._message, messageId, options.l10nArgs);
if (options.descriptionId) {
document.l10n.setAttributes(this._description, options.descriptionId);
this._description.hidden = false;
@@ -9236,11 +8036,9 @@ var FirefoxViewHandler = {
ChromeUtils.defineESModuleGetters(this, {
SyncedTabs: "resource://services-sync/SyncedTabs.sys.mjs",
});
- Services.obs.addObserver(this, "firefoxview-notification-dot-update");
},
uninit() {
CustomizableUI.removeListener(this);
- Services.obs.removeObserver(this, "firefoxview-notification-dot-update");
},
onWidgetRemoved(aWidgetId) {
if (aWidgetId == this.BUTTON_ID && this.tab) {
@@ -9293,7 +8091,7 @@ var FirefoxViewHandler = {
},
handleEvent(e) {
switch (e.type) {
- case "TabSelect":
+ case "TabSelect": {
const selected = e.target == this.tab;
this.button?.toggleAttribute("open", selected);
this.button?.setAttribute("aria-pressed", selected);
@@ -9304,6 +8102,7 @@ var FirefoxViewHandler = {
gBrowser.visibleTabs[0].style.MozUserFocus =
e.target == this.tab ? "normal" : "";
break;
+ }
case "TabClose":
this.tab = null;
gBrowser.tabContainer.removeEventListener("TabSelect", this);
@@ -9314,14 +8113,6 @@ var FirefoxViewHandler = {
break;
}
},
- observe(sub, topic, data) {
- switch (topic) {
- case "firefoxview-notification-dot-update":
- let shouldShow = data === "true";
- this._toggleNotificationDot(shouldShow);
- break;
- }
- },
_closeDeviceConnectedTab() {
if (!TabsSetupFlowManager.didFxaTabOpen) {
return;
@@ -9352,11 +8143,6 @@ var FirefoxViewHandler = {
_onTabForegrounded() {
if (this.tab?.selected) {
this.SyncedTabs.syncTabs();
- Services.obs.notifyObservers(
- null,
- "firefoxview-notification-dot-update",
- "false"
- );
}
},
_recordViewIfTabSelected() {
@@ -9380,7 +8166,4 @@ var FirefoxViewHandler = {
}
}
},
- _toggleNotificationDot(shouldShow) {
- this.button?.toggleAttribute("attention", shouldShow);
- },
};
diff --git a/browser/base/content/browser.js.globals b/browser/base/content/browser.js.globals
index 7002cd0b5b..910e0b5ca9 100644
--- a/browser/base/content/browser.js.globals
+++ b/browser/base/content/browser.js.globals
@@ -28,9 +28,6 @@
"gPopupBlockerObserver",
"gKeywordURIFixup",
"_createNullPrincipalFromTabUserContextId",
- "_resolveDelayedStartup",
- "delayedStartupPromise",
- "gBrowserInit",
"HandleAppCommandEvent",
"BrowserCommands",
"kSkipCacheFlags",
@@ -54,7 +51,6 @@
"BrowserSearch",
"CreateContainerTabMenu",
"FillHistoryMenu",
- "BrowserDownloadsUI",
"toOpenWindowByType",
"OpenBrowserWindow",
"updateEditUIVisibility",
@@ -85,7 +81,6 @@
"handleLinkClick",
"middleMousePaste",
"handleDroppedLink",
- "BrowserForceEncodingDetection",
"ToolbarContextMenu",
"BrowserOffline",
"CanvasPermissionPromptHelper",
@@ -94,7 +89,6 @@
"WindowIsClosing",
"warnAboutClosingWindow",
"MailIntegration",
- "BrowserOpenAddonsMgr",
"AddKeywordForSearchField",
"restoreLastClosedTabOrWindowOrSession",
"undoCloseTab",
@@ -135,7 +129,6 @@
"DownloadsCommon",
"E10SUtils",
"ExtensionsUI",
- "FirefoxViewNotificationManager",
"HomePage",
"isProductURL",
"LightweightThemeConsumer",
diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml
index 8ef14813b1..aec0983a67 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -39,7 +39,6 @@
<link rel="stylesheet" href="chrome://global/skin/global.css" />
<link rel="stylesheet" href="chrome://browser/content/browser.css" />
- <link rel="stylesheet" href="chrome://browser/content/tabbrowser.css" />
<link
rel="stylesheet"
href="chrome://browser/content/downloads/downloads.css"
@@ -84,7 +83,6 @@
<link rel="localization" href="browser/translations.ftl" />
<link rel="localization" href="browser/unifiedExtensions.ftl"/>
<link rel="localization" href="browser/webrtcIndicator.ftl"/>
- <link rel="localization" href="toolkit/branding/accounts.ftl"/>
<link rel="localization" href="toolkit/branding/brandings.ftl"/>
<link rel="localization" href="toolkit/global/contextual-identity.ftl"/>
<link rel="localization" href="toolkit/global/textActions.ftl"/>
@@ -107,6 +105,7 @@
<script>
/* eslint-env mozilla/browser-window */
+ Services.scriptloader.loadSubScript("chrome://browser/content/browser-init.js", this);
Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", this);
Services.scriptloader.loadSubScript("chrome://browser/content/browser-captivePortal.js", this);
if (AppConstants.MOZ_DATA_REPORTING) {
diff --git a/browser/base/content/macWindow.inc.xhtml b/browser/base/content/macWindow.inc.xhtml
index b9ca44c7eb..b5afa5f644 100644
--- a/browser/base/content/macWindow.inc.xhtml
+++ b/browser/base/content/macWindow.inc.xhtml
@@ -16,7 +16,6 @@
<html:link rel="localization" href="browser/menubar.ftl"/>
<html:link rel="localization" href="browser/reportBrokenSite.ftl"/>
<html:link rel="localization" href="browser/screenshots.ftl"/>
- <html:link rel="localization" href="toolkit/branding/accounts.ftl"/>
<html:link rel="localization" href="toolkit/branding/brandings.ftl"/>
<html:link rel="localization" href="toolkit/global/textActions.ftl"/>
</linkset>
diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml
index 91b2483c53..ef8245938e 100644
--- a/browser/base/content/main-popupset.inc.xhtml
+++ b/browser/base/content/main-popupset.inc.xhtml
@@ -77,13 +77,16 @@
data-lazy-l10n-id="tab-context-close-n-tabs"
data-l10n-args='{"tabCount": 1}'
oncommand="TabContextMenu.closeContextTabs();"/>
+ <menuitem id="context_closeDuplicateTabs"
+ data-lazy-l10n-id="tab-context-close-duplicate-tabs"
+ oncommand="gBrowser.removeDuplicateTabs(TabContextMenu.contextTab);"/>
<menu id="context_closeTabOptions"
data-lazy-l10n-id="tab-context-close-multiple-tabs">
<menupopup id="closeTabOptions">
<menuitem id="context_closeTabsToTheStart" data-lazy-l10n-id="close-tabs-to-the-start"
- oncommand="gBrowser.removeTabsToTheStartFrom(TabContextMenu.contextTab, {animate: true});"/>
+ oncommand="gBrowser.removeTabsToTheStartFrom(TabContextMenu.contextTab);"/>
<menuitem id="context_closeTabsToTheEnd" data-lazy-l10n-id="close-tabs-to-the-end"
- oncommand="gBrowser.removeTabsToTheEndFrom(TabContextMenu.contextTab, {animate: true});"/>
+ oncommand="gBrowser.removeTabsToTheEndFrom(TabContextMenu.contextTab);"/>
<menuitem id="context_closeOtherTabs" data-lazy-l10n-id="close-other-tabs"
oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
</menupopup>
@@ -253,26 +256,26 @@
<menuitem id="sidebar-switcher-bookmarks"
data-l10n-id="sidebar-menu-bookmarks"
key="viewBookmarksSidebarKb"
- oncommand="SidebarUI.show('viewBookmarksSidebar');"/>
+ oncommand="SidebarController.show('viewBookmarksSidebar');"/>
<menuitem id="sidebar-switcher-history"
data-l10n-id="sidebar-menu-history"
key="key_gotoHistory"
- oncommand="SidebarUI.show('viewHistorySidebar');"/>
+ oncommand="SidebarController.show('viewHistorySidebar');"/>
<menuitem id="sidebar-switcher-tabs"
data-l10n-id="sidebar-menu-synced-tabs"
class="sync-ui-item"
- oncommand="SidebarUI.show('viewTabsSidebar');"/>
+ oncommand="SidebarController.show('viewTabsSidebar');"/>
<menuitem id="sidebar-switcher-megalist"
data-l10n-id="sidebar-menu-megalist"
- oncommand="SidebarUI.show('viewMegalistSidebar');"/>
+ oncommand="SidebarController.show('viewMegalistSidebar');"/>
<menuseparator/>
<!-- Extension toolbarbuttons go here. -->
<menuseparator id="sidebar-extensions-separator"/>
<menuitem id="sidebar-reverse-position"
- oncommand="SidebarUI.reversePosition()"/>
+ oncommand="SidebarController.reversePosition()"/>
<menuseparator/>
<menuitem data-l10n-id="sidebar-menu-close"
- oncommand="SidebarUI.hide()"/>
+ oncommand="SidebarController.hide()"/>
</menupopup>
<menupopup id="toolbar-context-menu"
@@ -426,6 +429,10 @@
<html:div class="tab-preview-text-container">
<html:div class="tab-preview-title"></html:div>
<html:div class="tab-preview-uri"></html:div>
+ <html:div class="tab-preview-pid-activeness">
+ <html:div class="tab-preview-pid"></html:div>
+ <html:div class="tab-preview-activeness"></html:div>
+ </html:div>
</html:div>
<html:div class="tab-preview-thumbnail-container"></html:div>
</panel>
@@ -675,4 +682,14 @@
<menuitem data-l10n-id="translations-panel-settings-about2"
oncommand="FullPageTranslationsPanel.onAboutTranslations()"/>
</menupopup>
+
+ <menupopup id="select-translations-panel-settings-menupopup">
+ <menuitem id="select-translations-panel-open-settings-page-menuitem"
+ class="manage-languages-menuitem"
+ data-l10n-id="select-translations-panel-open-translations-settings-menuitem"
+ oncommand="SelectTranslationsPanel.openTranslationsSettingsPage()"/>
+ <menuitem id="select-translations-panel-about-translations-menuitem"
+ data-l10n-id="translations-panel-settings-about2"
+ oncommand="SelectTranslationsPanel.onAboutTranslations()"/>
+ </menupopup>
</popupset>
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index a86d01adb2..0a67afa81f 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -100,6 +100,9 @@
<image class="private-browsing-indicator-icon"/>
<label data-l10n-id="private-browsing-indicator-label"></label>
</hbox>
+ <toolbarbutton id="content-analysis-indicator"
+ oncommand="ContentAnalysis.showPanel(this, PanelUI);"
+ class="toolbarbutton-1 content-analysis-indicator-icon"/>
#include titlebar-items.inc.xhtml
@@ -509,12 +512,12 @@
customizable="true">
<toolbartabstop skipintoolbarset="true"/>
- <hbox id="personal-toolbar-empty" skipintoolbarset="true" removable="false" hidden="true">
+ <hbox id="personal-toolbar-empty" skipintoolbarset="true" removable="false" hidden="true" role="alert">
<description id="personal-toolbar-empty-description"
data-l10n-id="bookmarks-toolbar-empty-message"
onclick="BookmarkingUI.openLibraryIfLinkClicked(event);"
onkeydown="BookmarkingUI.openLibraryIfLinkClicked(event);">
- <html:a data-l10n-name="manage-bookmarks" class="text-link" tabindex="0"/>
+ <html:a data-l10n-name="manage-bookmarks" class="text-link" tabindex="0" role="link"/>
</description>
</hbox>
@@ -618,7 +621,7 @@
<menuitem id="BMB_viewBookmarksSidebar"
data-l10n-id="bookmarks-tools-sidebar-visibility"
data-l10n-args='{ "isVisible": false }'
- oncommand="SidebarUI.toggle('viewBookmarksSidebar');"
+ oncommand="SidebarController.toggle('viewBookmarksSidebar');"
key="viewBookmarksSidebarKb"/>
<menuitem id="BMB_searchBookmarks"
data-l10n-id="bookmarks-search"
diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js
index 9769552dce..1811dfcaca 100644
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1347,8 +1347,6 @@ class nsContextMenu {
!this.onTextInput &&
!this.onLink &&
!this.onPlainTextLink &&
- !this.onImage &&
- !this.onVideo &&
!this.onAudio &&
!this.onEditable &&
!this.onPassword;
@@ -1685,7 +1683,7 @@ class nsContextMenu {
// Change current window to the URL of the image, video, or audio.
viewMedia(e) {
- let where = whereToOpenLink(e, false, false);
+ let where = BrowserUtils.whereToOpenLink(e, false, false);
if (where == "current") {
where = "tab";
}
@@ -2175,7 +2173,10 @@ class nsContextMenu {
var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
Ci.nsIClipboardHelper
);
- clipboard.copyString(addresses);
+ clipboard.copyString(
+ addresses,
+ this.actor.manager.browsingContext.currentWindowGlobal
+ );
}
// Extract phone and put it on clipboard
@@ -2195,7 +2196,10 @@ class nsContextMenu {
var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
Ci.nsIClipboardHelper
);
- clipboard.copyString(phone);
+ clipboard.copyString(
+ phone,
+ this.actor.manager.browsingContext.currentWindowGlobal
+ );
}
copyLink() {
@@ -2204,7 +2208,10 @@ class nsContextMenu {
var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
Ci.nsIClipboardHelper
);
- clipboard.copyString(linkURL);
+ clipboard.copyString(
+ linkURL,
+ this.actor.manager.browsingContext.currentWindowGlobal
+ );
}
/**
@@ -2220,7 +2227,10 @@ class nsContextMenu {
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
Ci.nsIClipboardHelper
);
- clipboard.copyString(strippedLinkURL);
+ clipboard.copyString(
+ strippedLinkURL,
+ this.actor.manager.browsingContext.currentWindowGlobal
+ );
}
}
@@ -2324,8 +2334,8 @@ class nsContextMenu {
try {
strippedLinkURI = QueryStringStripper.stripForCopyOrShare(this.linkURI);
} catch (e) {
- console.warn(`isLinkURIStrippable: ${e.message}`);
- return null;
+ console.warn(`stripForCopyOrShare: ${e.message}`);
+ return this.linkURI;
}
// If nothing can be stripped, we return the original URI
@@ -2458,7 +2468,10 @@ class nsContextMenu {
var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
Ci.nsIClipboardHelper
);
- clipboard.copyString(this.originalMediaURL);
+ clipboard.copyString(
+ this.originalMediaURL,
+ this.actor.manager.browsingContext.currentWindowGlobal
+ );
}
getImageText() {
@@ -2484,7 +2497,7 @@ class nsContextMenu {
let drmInfoURL =
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"drm-content";
- let dest = whereToOpenLink(aEvent);
+ let dest = BrowserUtils.whereToOpenLink(aEvent);
// Don't ever want this to open in the same tab as it'll unload the
// DRM'd video, which is going to be a bad idea in most cases.
if (dest == "current") {
@@ -2526,7 +2539,7 @@ class nsContextMenu {
screenY,
this.#getTextToTranslate(),
this.#translationsLangPairPromise
- );
+ ).catch(console.error);
}
/**
@@ -2606,6 +2619,8 @@ class nsContextMenu {
translateSelectionItem.hidden =
// Only show the item if the feature is enabled.
!(translationsEnabled && selectTranslationsEnabled) ||
+ // Only show the item if Translations is supported on this hardware.
+ !TranslationsParent.getIsTranslationsEngineSupported() ||
// If there is no text to translate, we have nothing to do.
textToTranslate.length === 0 ||
// We do not allow translating selections on top of Full Page Translations.
diff --git a/browser/base/content/popup-notifications.inc b/browser/base/content/popup-notifications.inc
index daee34e6fe..3d57f4808c 100644
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -35,7 +35,7 @@
<description id="webRTC-all-windows-shared" hidden="true" data-l10n-id="popup-all-windows-shared"></description>
</popupnotificationcontent>
- <popupnotificationcontent id="webRTC-preview" hidden="true">
+ <popupnotificationcontent id="webRTC-preview" orient="vertical" hidden="true">
<html:video id="webRTC-previewVideo" tabindex="-1"/>
<vbox id="webRTC-previewWarningBox">
<description id="webRTC-previewWarning"/>
diff --git a/browser/base/content/spotlight.html b/browser/base/content/spotlight.html
index a948f8dbf4..216f290efd 100644
--- a/browser/base/content/spotlight.html
+++ b/browser/base/content/spotlight.html
@@ -19,6 +19,7 @@
<link rel="localization" href="branding/brand.ftl" />
<link rel="localization" href="toolkit/branding/brandings.ftl" />
<link rel="localization" href="browser/newtab/asrouter.ftl" />
+ <link rel="localization" href="preview/onboarding.ftl" />
<link rel="localization" href="browser/newtab/onboarding.ftl" />
<link rel="localization" href="browser/spotlight.ftl" />
<link rel="localization" href="browser/migrationWizard.ftl" />
diff --git a/browser/base/content/tabbrowser-tabs.js b/browser/base/content/tabbrowser-tabs.js
index 7784e74ed3..9b30584077 100644
--- a/browser/base/content/tabbrowser-tabs.js
+++ b/browser/base/content/tabbrowser-tabs.js
@@ -125,23 +125,14 @@
this.tabbox.tabpanels.setAttribute("async", "true");
}
- this.configureTooltip = () => {
- // fall back to original tooltip behavior if pref is not set
- if (this._showCardPreviews) {
- this.tooltip = null;
- } else {
- this.tooltip = "tabbrowser-tab-tooltip";
- this._previewPanel = null;
- }
- };
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_showCardPreviews",
TAB_PREVIEW_PREF,
- false,
- () => this.configureTooltip()
+ false
);
- this.configureTooltip();
+ this.tooltip = "tabbrowser-tab-tooltip";
+ this._previewPanel = null;
}
on_TabSelect() {
diff --git a/browser/base/content/tabbrowser.css b/browser/base/content/tabbrowser.css
deleted file mode 100644
index 120203141c..0000000000
--- a/browser/base/content/tabbrowser.css
+++ /dev/null
@@ -1,101 +0,0 @@
-/* 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/. */
-
-.tab-close-button[pinned],
-#tabbrowser-tabs[closebuttons="activetab"] > #tabbrowser-arrowscrollbox > .tabbrowser-tab > .tab-stack > .tab-content > .tab-close-button:not([selected]),
-.tab-icon-pending:not([pendingicon]),
-.tab-icon-pending[busy],
-.tab-icon-pending[pinned],
-.tab-icon-image:not([src], [pinned], [crashed], [pictureinpicture])[selected],
-.tab-icon-image:not([src], [pinned], [crashed], [sharing], [pictureinpicture]),
-.tab-icon-image[busy],
-.tab-throbber:not([busy]),
-.tab-sharing-icon-overlay,
-.tab-icon-overlay {
- display: none;
-}
-
-:root[uidensity=compact] .tab-secondary-label,
-.tab-secondary-label:not([soundplaying], [muted], [activemedia-blocked], [pictureinpicture]),
-.tab-secondary-label:not([activemedia-blocked]) > .tab-icon-sound-blocked-label,
-.tab-secondary-label[muted][activemedia-blocked] > .tab-icon-sound-blocked-label,
-.tab-secondary-label[activemedia-blocked] > .tab-icon-sound-playing-label,
-.tab-secondary-label[muted] > .tab-icon-sound-playing-label,
-.tab-secondary-label[pictureinpicture] > .tab-icon-sound-playing-label,
-.tab-secondary-label[pictureinpicture] > .tab-icon-sound-muted-label,
-.tab-secondary-label:not([pictureinpicture]) > .tab-icon-sound-pip-label,
-.tab-secondary-label:not([muted]) > .tab-icon-sound-muted-label,
-.tab-secondary-label:not([showtooltip]) > .tab-icon-sound-tooltip-label,
-.tab-secondary-label[showtooltip] > .tab-icon-sound-label:not(.tab-icon-sound-tooltip-label) {
- display: none;
-}
-
-.tab-sharing-icon-overlay[sharing]:not([selected]),
-.tab-icon-overlay:is([soundplaying], [muted], [activemedia-blocked], [crashed]) {
- display: revert;
-}
-
-.tabbrowser-tab {
- --tab-label-mask-size: 2em;
-}
-
-.tab-label {
- white-space: nowrap;
- line-height: 1.7; /* override 'normal' in case of fallback math fonts with huge metrics */
-}
-
-.tab-secondary-label {
- margin: -.3em 0 .3em; /* adjust margins to compensate for line-height of .tab-label */
-}
-
-.tab-label-container {
- overflow: hidden;
-}
-
-.tab-label-container[pinned] {
- width: 0;
-}
-
-.tab-label-container[textoverflow][labeldirection=ltr]:not([pinned]),
-.tab-label-container[textoverflow]:not([labeldirection], [pinned]):-moz-locale-dir(ltr) {
- direction: ltr;
- mask-image: linear-gradient(to left, transparent, black var(--tab-label-mask-size));
-}
-
-.tab-label-container[textoverflow][labeldirection=rtl]:not([pinned]),
-.tab-label-container[textoverflow]:not([labeldirection], [pinned]):-moz-locale-dir(rtl) {
- direction: rtl;
- mask-image: linear-gradient(to right, transparent, black var(--tab-label-mask-size));
-}
-
-tabpanels {
- background-color: transparent;
-}
-
-/* Apply crisp rendering for favicons at exactly 2dppx resolution */
-@media (resolution: 2dppx) {
- .tab-icon-image {
- image-rendering: -moz-crisp-edges;
- }
-}
-
-.closing-tabs-spacer {
- pointer-events: none;
-}
-
-#tabbrowser-arrowscrollbox:not(:hover) > #tabbrowser-arrowscrollbox-periphery > .closing-tabs-spacer {
- transition: width .15s ease-out;
-}
-
-browser[blank],
-browser[pendingpaint] {
- opacity: 0;
-}
-
-#tabbrowser-tabpanels[pendingpaint] {
- background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
- background-repeat: no-repeat;
- background-position: center center;
- background-size: 30px;
-}
diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js
index 3bca0b6d30..f319fd5d46 100644
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -67,7 +67,7 @@
replaceContainerClass("color", hbox, identity.color);
let label = ContextualIdentityService.getUserContextLabel(userContextId);
- document.getElementById("userContext-label").setAttribute("value", label);
+ document.getElementById("userContext-label").textContent = label;
// Also set the container label as the tooltip so we can only show the icon
// in small windows.
hbox.setAttribute("tooltiptext", label);
@@ -110,6 +110,12 @@
"privacy.exposeContentTitleInWindow.pbm",
true
);
+ XPCOMUtils.defineLazyPreferenceGetter(
+ this,
+ "_showTabCardPreview",
+ "browser.tabs.cardPreview.enabled",
+ true
+ );
if (AppConstants.MOZ_CRASHREPORTER) {
ChromeUtils.defineESModuleGetters(this, {
@@ -161,6 +167,8 @@
TO_START: 2,
TO_END: 3,
MULTI_SELECTED: 4,
+ DUPLICATES: 6,
+ ALL_DUPLICATES: 7,
},
_lastRelatedTabMap: new WeakMap(),
@@ -348,6 +356,90 @@
return this.tabContainer._getVisibleTabs();
},
+ getDuplicateTabsToClose(aTab) {
+ // One would think that a set is better, but it would need to copy all
+ // the strings instead of just keeping references to the nsIURI objects,
+ // and the array is presumed to be small anyways.
+ let keys = [];
+ let keyForTab = tab => {
+ let uri = tab.linkedBrowser?.currentURI;
+ if (!uri) {
+ return null;
+ }
+ return {
+ uri,
+ userContextId: tab.userContextId,
+ };
+ };
+ let keyEquals = (a, b) => {
+ return a.userContextId == b.userContextId && a.uri.equals(b.uri);
+ };
+ if (aTab.multiselected) {
+ for (let tab of this.selectedTabs) {
+ let key = keyForTab(tab);
+ if (key) {
+ keys.push(key);
+ }
+ }
+ } else {
+ let key = keyForTab(aTab);
+ if (key) {
+ keys.push(key);
+ }
+ }
+
+ if (!keys.length) {
+ return [];
+ }
+
+ let duplicateTabs = [];
+ for (let tab of this.tabs) {
+ if (tab == aTab || tab.pinned) {
+ continue;
+ }
+ if (aTab.multiselected && tab.multiselected) {
+ continue;
+ }
+ let key = keyForTab(tab);
+ if (key && keys.some(k => keyEquals(k, key))) {
+ duplicateTabs.push(tab);
+ }
+ }
+
+ return duplicateTabs;
+ },
+
+ getAllDuplicateTabsToClose() {
+ let lastSeenTabs = this.tabs.toSorted(
+ (a, b) => b.lastSeenActive - a.lastSeenActive
+ );
+ let duplicateTabs = [];
+ let keys = [];
+ for (let tab of lastSeenTabs) {
+ const uri = tab.linkedBrowser?.currentURI;
+ if (!uri) {
+ // Can't tell if it's a duplicate without a URI.
+ // Safest to leave it be.
+ continue;
+ }
+
+ const key = {
+ uri,
+ userContextId: tab.userContextId,
+ };
+ if (
+ !tab.pinned &&
+ keys.some(
+ k => k.userContextId == key.userContextId && k.uri.equals(key.uri)
+ )
+ ) {
+ duplicateTabs.push(tab);
+ }
+ keys.push(key);
+ }
+ return duplicateTabs;
+ },
+
get _numPinnedTabs() {
for (var i = 0; i < this.tabs.length; i++) {
if (!this.tabs[i].pinned) {
@@ -1108,6 +1200,11 @@
return;
}
+ let oldBrowser = this.selectedBrowser;
+ // Once the async switcher starts, it's unpredictable when it will touch
+ // the address bar, thus we store its state immediately.
+ gURLBar?.saveSelectionStateForBrowser(oldBrowser);
+
let newTab = this.getTabForBrowser(newBrowser);
if (!aForceUpdate) {
@@ -1137,8 +1234,6 @@
}
this._lastRelatedTabMap = new WeakMap();
- let oldBrowser = this.selectedBrowser;
-
if (!gMultiProcessBrowser) {
oldBrowser.removeAttribute("primary");
oldBrowser.docShellIsActive = false;
@@ -1146,11 +1241,6 @@
newBrowser.docShellIsActive = !document.hidden;
}
- if (gURLBar) {
- oldBrowser._urlbarSelectionStart = gURLBar.selectionStart;
- oldBrowser._urlbarSelectionEnd = gURLBar.selectionEnd;
- }
-
this._selectedBrowser = newBrowser;
this._selectedTab = newTab;
this.showTab(newTab);
@@ -1445,19 +1535,12 @@
if (currentActiveElement != document.activeElement) {
return;
}
-
- gURLBar.setSelectionRange(
- newBrowser._urlbarSelectionStart,
- newBrowser._urlbarSelectionEnd
- );
+ gURLBar.restoreSelectionStateForBrowser(newBrowser);
},
{ once: true }
);
} else {
- gURLBar.setSelectionRange(
- newBrowser._urlbarSelectionStart,
- newBrowser._urlbarSelectionEnd
- );
+ gURLBar.restoreSelectionStateForBrowser(newBrowser);
}
};
@@ -1610,6 +1693,22 @@
_dataURLRegEx: /^data:[^,]+;base64,/i,
+ // Regex to test if a string (potential tab label) consists of only non-
+ // printable characters. We consider Unicode categories Separator
+ // (spaces & line-breaks) and Other (control chars, private use, non-
+ // character codepoints) to be unprintable, along with a few specific
+ // characters whose expected rendering is blank:
+ // U+2800 BRAILLE PATTERN BLANK (category So)
+ // U+115F HANGUL CHOSEONG FILLER (category Lo)
+ // U+1160 HANGUL JUNGSEONG FILLER (category Lo)
+ // U+3164 HANGUL FILLER (category Lo)
+ // U+FFA0 HALFWIDTH HANGUL FILLER (category Lo)
+ // We also ignore combining marks, as in the absence of a printable base
+ // character they are unlikely to be usefully rendered, and may well be
+ // clipped away entirely.
+ _nonPrintingRegEx:
+ /^[\p{Z}\p{C}\p{M}\u{115f}\u{1160}\u{2800}\u{3164}\u{ffa0}]*$/u,
+
setTabTitle(aTab) {
var browser = this.getBrowserForTab(aTab);
var title = browser.contentTitle;
@@ -1630,6 +1729,16 @@
}
let isURL = false;
+
+ // Trim leading and trailing whitespace from the title.
+ title = title.trim();
+
+ // If the title contains only non-printing characters (or only combining
+ // marks, but no base character for them), we won't use it.
+ if (this._nonPrintingRegEx.test(title)) {
+ title = "";
+ }
+
let isContentTitle = !!title;
if (!title) {
// See if we can use the URI as the title.
@@ -2118,16 +2227,12 @@
b.setAttribute("name", name);
}
- let notificationbox = document.createXULElement("notificationbox");
- notificationbox.setAttribute("notificationside", "top");
-
let stack = document.createXULElement("stack");
stack.className = "browserStack";
stack.appendChild(b);
let browserContainer = document.createXULElement("vbox");
browserContainer.className = "browserContainer";
- browserContainer.appendChild(notificationbox);
browserContainer.appendChild(stack);
let browserSidebarContainer = document.createXULElement("hbox");
@@ -3244,6 +3349,24 @@
return true;
}
+ const shownDupeDialogPref =
+ "browser.tabs.haveShownCloseAllDuplicateTabsWarning";
+ if (
+ aCloseTabs == this.closingTabsEnum.ALL_DUPLICATES &&
+ !Services.prefs.getBoolPref(shownDupeDialogPref, false)
+ ) {
+ // The first time a user closes all duplicate tabs, tell them what will
+ // happen and give them a chance to back away.
+ Services.prefs.setBoolPref(shownDupeDialogPref, true);
+
+ window.focus();
+ const [title, text] = this.tabLocalization.formatValuesSync([
+ { id: "tabbrowser-confirm-close-duplicate-tabs-title" },
+ { id: "tabbrowser-confirm-close-duplicate-tabs-text" },
+ ]);
+ return Services.prompt.confirm(window, title, text);
+ }
+
const pref =
aCloseTabs == this.closingTabsEnum.ALL
? "browser.tabs.warnOnClose"
@@ -3483,6 +3606,42 @@
return tabsToEnd;
},
+ removeDuplicateTabs(aTab) {
+ this._removeDuplicateTabs(
+ aTab,
+ this.getDuplicateTabsToClose(aTab),
+ this.closingTabsEnum.DUPLICATES
+ );
+ },
+
+ _removeDuplicateTabs(aConfirmationAnchor, tabs, aCloseTabs) {
+ if (!tabs.length) {
+ return;
+ }
+
+ if (!this.warnAboutClosingTabs(tabs.length, aCloseTabs)) {
+ return;
+ }
+
+ this.removeTabs(tabs);
+ ConfirmationHint.show(
+ aConfirmationAnchor,
+ "confirmation-hint-duplicate-tabs-closed",
+ { l10nArgs: { tabCount: tabs.length } }
+ );
+ },
+
+ removeAllDuplicateTabs() {
+ // I would like to have the caller provide this target,
+ // but the caller lives in a different document.
+ let alltabsButton = document.getElementById("alltabs-button");
+ this._removeDuplicateTabs(
+ alltabsButton,
+ this.getAllDuplicateTabsToClose(),
+ this.closingTabsEnum.ALL_DUPLICATES
+ );
+ },
+
/**
* In a multi-select context, the tabs (except pinned tabs) that are located to the
* left of the leftmost selected tab will be removed.
@@ -4330,6 +4489,56 @@
);
}
},
+ /**
+ * Closes tabs within the browser that match a given list of nsURIs. Returns
+ * any nsURIs that could not be closed successfully. This does not close any
+ * tabs that have a beforeUnload prompt
+ *
+ * @param {nsURI[]} urisToClose
+ * The set of uris to remove.
+ * @returns {nsURI[]}
+ * the nsURIs that weren't found in this browser
+ */
+ async closeTabsByURI(urisToClose) {
+ let remainingURIsToClose = [...urisToClose];
+ let tabsToRemove = [];
+ for (let tab of this.tabs) {
+ let currentURI = tab.linkedBrowser.currentURI;
+ // Find any URI that matches the current tab's URI
+ const matchedIndex = remainingURIsToClose.findIndex(uriToClose =>
+ uriToClose.equals(currentURI)
+ );
+
+ if (matchedIndex > -1) {
+ tabsToRemove.push(tab);
+ remainingURIsToClose.splice(matchedIndex, 1); // Remove the matched URI
+ }
+ }
+
+ if (tabsToRemove.length) {
+ const { beforeUnloadComplete, lastToClose } = this._startRemoveTabs(
+ tabsToRemove,
+ {
+ animate: false,
+ suppressWarnAboutClosingWindow: true,
+ skipPermitUnload: false,
+ skipRemoves: false,
+ skipSessionStore: false,
+ }
+ );
+
+ // Wait for the beforeUnload handlers to complete.
+ await beforeUnloadComplete;
+
+ // _startRemoveTabs doesn't close the last tab in the window
+ // for this use case, we simply close it
+ if (lastToClose) {
+ this.removeTab(lastToClose);
+ }
+ }
+ // If we still have uris, that means we couldn't find them in this window instance
+ return remainingURIsToClose;
+ },
/**
* Handles opening a new tab with mouse middleclick.
@@ -5659,6 +5868,20 @@
}
},
+ getTabPids(tab) {
+ if (!tab.linkedBrowser) {
+ return [];
+ }
+
+ // Get the PIDs of the content process and remote subframe processes
+ let [contentPid, ...framePids] = E10SUtils.getBrowserPids(
+ tab.linkedBrowser,
+ gFissionBrowser
+ );
+ let pids = contentPid ? [contentPid] : [];
+ return pids.concat(framePids.sort());
+ },
+
getTabTooltip(tab, includeLabel = true) {
let labelArray = [];
if (includeLabel) {
@@ -5670,24 +5893,14 @@
false
)
) {
- if (tab.linkedBrowser) {
- // Show the PIDs of the content process and remote subframe processes.
- let [contentPid, ...framePids] = E10SUtils.getBrowserPids(
- tab.linkedBrowser,
- gFissionBrowser
- );
- if (contentPid) {
- if (framePids && framePids.length) {
- labelArray.push(
- `(pids ${contentPid}, ${framePids.sort().join(", ")})`
- );
- } else {
- labelArray.push(`(pid ${contentPid})`);
- }
- }
- if (tab.linkedBrowser.docShellIsActive) {
- labelArray.push("[A]");
- }
+ const pids = this.getTabPids(tab);
+ if (pids.length) {
+ let pidLabel = pids.length > 1 ? "pids" : "pid";
+ labelArray.push(`(${pidLabel} ${pids.join(", ")})`);
+ }
+
+ if (tab.linkedBrowser.docShellIsActive) {
+ labelArray.push("[A]");
}
}
@@ -5750,6 +5963,13 @@
tooltip.label = "";
document.l10n.setAttributes(tooltip, l10nId, l10nArgs);
} else {
+ // Prevent the tooltip from appearing if card preview is enabled, but
+ // only if the user is not hovering over the media play icon or the
+ // close button
+ if (this._showTabCardPreview) {
+ event.preventDefault();
+ return;
+ }
tooltip.label = this.getTabTooltip(tab, true);
}
},
@@ -7528,9 +7748,8 @@ var TabContextMenu = {
tabsToMove[0] == visibleTabs[gBrowser._numPinnedTabs];
contextMoveTabToStart.disabled = isFirstTab && allSelectedTabsAdjacent;
- if (this.contextTab.hasAttribute("customizemode")) {
- document.getElementById("context_openTabInWindow").disabled = true;
- }
+ document.getElementById("context_openTabInWindow").disabled =
+ this.contextTab.hasAttribute("customizemode");
// Only one of "Duplicate Tab"/"Duplicate Tabs" should be visible.
document.getElementById("context_duplicateTab").hidden =
@@ -7564,6 +7783,17 @@ var TabContextMenu = {
.getElementById("context_closeTab")
.setAttribute("data-l10n-args", tabCountInfo);
+ let closeDuplicateEnabled = Services.prefs.getBoolPref(
+ "browser.tabs.context.close-duplicate.enabled"
+ );
+ let closeDuplicateTabsItem = document.getElementById(
+ "context_closeDuplicateTabs"
+ );
+ closeDuplicateTabsItem.hidden = !closeDuplicateEnabled;
+ closeDuplicateTabsItem.disabled =
+ !closeDuplicateEnabled ||
+ !gBrowser.getDuplicateTabsToClose(this.contextTab).length;
+
// Disable "Close Multiple Tabs" if all sub menuitems are disabled
document.getElementById("context_closeTabOptions").disabled =
closeTabsToTheStartItem.disabled &&
diff --git a/browser/base/content/test/about/browser.toml b/browser/base/content/test/about/browser.toml
index 98961200a0..2c6dafb4dd 100644
--- a/browser/base/content/test/about/browser.toml
+++ b/browser/base/content/test/about/browser.toml
@@ -66,7 +66,6 @@ support-files = [
["browser_aboutNewTab_bookmarksToolbar.js"]
["browser_aboutNewTab_bookmarksToolbarEmpty.js"]
-fail-if = ["a11y_checks"] # Bug 1854233 text-link may not be focusable
skip-if = ["tsan"] # Bug 1676326, highly frequent on TSan
["browser_aboutNewTab_bookmarksToolbarNewWindow.js"]
diff --git a/browser/base/content/test/about/browser_aboutNetError_csp_iframe.js b/browser/base/content/test/about/browser_aboutNetError_csp_iframe.js
index c8028a4cf4..d245d0cd3c 100644
--- a/browser/base/content/test/about/browser_aboutNetError_csp_iframe.js
+++ b/browser/base/content/test/about/browser_aboutNetError_csp_iframe.js
@@ -8,6 +8,10 @@ const BLOCKED_PAGE =
"http://example.org:8000/browser/browser/base/content/test/about/csp_iframe.sjs";
add_task(async function test_csp() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.xfocsp.hideOpenInNewWindow", false]],
+ });
+
let { iframePageTab, blockedPageTab } = await setupPage(
"iframe_page_csp.html",
BLOCKED_PAGE
diff --git a/browser/base/content/test/about/browser_aboutNetError_xfo_iframe.js b/browser/base/content/test/about/browser_aboutNetError_xfo_iframe.js
index f5fd240643..2373bd8b50 100644
--- a/browser/base/content/test/about/browser_aboutNetError_xfo_iframe.js
+++ b/browser/base/content/test/about/browser_aboutNetError_xfo_iframe.js
@@ -8,6 +8,10 @@ const BLOCKED_PAGE =
"http://example.org:8000/browser/browser/base/content/test/about/xfo_iframe.sjs";
add_task(async function test_xfo_iframe() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.xfocsp.hideOpenInNewWindow", false]],
+ });
+
let { iframePageTab, blockedPageTab } = await setupPage(
"iframe_page_xfo.html",
BLOCKED_PAGE
diff --git a/browser/base/content/test/alerts/browser.toml b/browser/base/content/test/alerts/browser.toml
index d0d56f7392..aaca2ba7dc 100644
--- a/browser/base/content/test/alerts/browser.toml
+++ b/browser/base/content/test/alerts/browser.toml
@@ -19,10 +19,6 @@ skip-if = ["os == 'win'"] # Bug 1411118
https_first_disabled = true
skip-if = ["os == 'win'"] # Bug 1411118
-["browser_notification_replace.js"]
-https_first_disabled = true
-skip-if = ["os == 'win'"] # Bug 1422928
-
["browser_notification_tab_switching.js"]
https_first_disabled = true
skip-if = ["os == 'win'"] # Bug 1243263
diff --git a/browser/base/content/test/alerts/browser_notification_close.js b/browser/base/content/test/alerts/browser_notification_close.js
index 7568f1cc2d..3fd50bed5b 100644
--- a/browser/base/content/test/alerts/browser_notification_close.js
+++ b/browser/base/content/test/alerts/browser_notification_close.js
@@ -21,17 +21,16 @@ add_task(async function test_notificationClose() {
Services.prefs.setBoolPref("alerts.showFavicons", true);
await PlacesTestUtils.addVisits(notificationURI);
- let faviconURI = await new Promise(resolve => {
- let uri = makeURI(
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC"
- );
- PlacesUtils.favicons.setAndFetchFaviconForPage(
+ let dataURL = makeURI(
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC"
+ );
+ await new Promise(resolve => {
+ PlacesUtils.favicons.setFaviconForPage(
notificationURI,
- uri,
- true,
- PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
- uriResult => resolve(uriResult),
- Services.scriptSecurityManager.getSystemPrincipal()
+ dataURL,
+ dataURL,
+ null,
+ resolve
);
});
@@ -67,11 +66,7 @@ add_task(async function test_notificationClose() {
"Body text of notification should be present"
);
let alertIcon = alertWindow.document.getElementById("alertIcon");
- is(
- alertIcon.src,
- faviconURI.spec,
- "Icon of notification should be present"
- );
+ is(alertIcon.src, dataURL.spec, "Icon of notification should be present");
let alertCloseButton = alertWindow.document.querySelector(".close-icon");
is(alertCloseButton.localName, "toolbarbutton", "close button found");
diff --git a/browser/base/content/test/alerts/browser_notification_replace.js b/browser/base/content/test/alerts/browser_notification_replace.js
deleted file mode 100644
index 9c72e90ab1..0000000000
--- a/browser/base/content/test/alerts/browser_notification_replace.js
+++ /dev/null
@@ -1,66 +0,0 @@
-"use strict";
-
-let notificationURL =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
- "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html";
-
-add_task(async function test_notificationReplace() {
- await addNotificationPermission(notificationURL);
-
- await BrowserTestUtils.withNewTab(
- {
- gBrowser,
- url: notificationURL,
- },
- async function dummyTabTask(aBrowser) {
- await SpecialPowers.spawn(aBrowser, [], async function () {
- let win = content.window.wrappedJSObject;
- let notification = win.showNotification1();
- let promiseCloseEvent = ContentTaskUtils.waitForEvent(
- notification,
- "close"
- );
-
- let showEvent = await ContentTaskUtils.waitForEvent(
- notification,
- "show"
- );
- Assert.equal(
- showEvent.target.body,
- "Test body 1",
- "Showed tagged notification"
- );
-
- let newNotification = win.showNotification2();
- let newShowEvent = await ContentTaskUtils.waitForEvent(
- newNotification,
- "show"
- );
- Assert.equal(
- newShowEvent.target.body,
- "Test body 2",
- "Showed new notification with same tag"
- );
-
- let closeEvent = await promiseCloseEvent;
- Assert.equal(
- closeEvent.target.body,
- "Test body 1",
- "Closed previous tagged notification"
- );
-
- let promiseNewCloseEvent = ContentTaskUtils.waitForEvent(
- newNotification,
- "close"
- );
- newNotification.close();
- let newCloseEvent = await promiseNewCloseEvent;
- Assert.equal(
- newCloseEvent.target.body,
- "Test body 2",
- "Closed new notification"
- );
- });
- }
- );
-});
diff --git a/browser/base/content/test/contextMenu/browser.toml b/browser/base/content/test/contextMenu/browser.toml
index 660f6a955b..c7af1bc437 100644
--- a/browser/base/content/test/contextMenu/browser.toml
+++ b/browser/base/content/test/contextMenu/browser.toml
@@ -101,3 +101,5 @@ support-files = [
"test_view_image_inline_svg.html",
]
skip-if = ["os == 'linux' && socketprocess_networking"]
+
+["browser_contextmenu_cross_boundary_selection.js"]
diff --git a/browser/base/content/test/contextMenu/browser_contextmenu.js b/browser/base/content/test/contextMenu/browser_contextmenu.js
index 00da3113c6..d8dc0ab5e0 100644
--- a/browser/base/content/test/contextMenu/browser_contextmenu.js
+++ b/browser/base/content/test/contextMenu/browser_contextmenu.js
@@ -41,6 +41,10 @@ let hasContainers =
Services.prefs.getBoolPref("privacy.userContext.enabled") &&
ContextualIdentityService.getPublicIdentities().length;
+const hasSelectTranslations =
+ Services.prefs.getBoolPref("browser.translations.enable") &&
+ Services.prefs.getBoolPref("browser.translations.select.enable");
+
const example_base =
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
"http://example.com/browser/browser/base/content/test/contextMenu/";
@@ -112,6 +116,7 @@ add_task(async function test_xul_text_link_label() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
// Clean up so won't affect HTML element test cases.
@@ -204,6 +209,7 @@ const kLinkItems = [
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
];
add_task(async function test_link() {
@@ -234,6 +240,7 @@ add_task(async function test_mailto() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
});
@@ -247,6 +254,7 @@ add_task(async function test_tel() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
});
@@ -273,6 +281,10 @@ add_task(async function test_image() {
null,
"context-setDesktopBackground",
true,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
],
{
onContextMenuShown() {
@@ -356,6 +368,10 @@ add_task(async function test_video_ok() {
true,
"context-sendvideo",
true,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
]);
await SpecialPowers.popPrefEnv();
@@ -404,6 +420,10 @@ add_task(async function test_video_ok() {
true,
"context-sendvideo",
true,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
]);
await SpecialPowers.popPrefEnv();
@@ -490,6 +510,10 @@ add_task(async function test_video_bad() {
true,
"context-sendvideo",
true,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
]);
await SpecialPowers.popPrefEnv();
@@ -538,6 +562,10 @@ add_task(async function test_video_bad() {
true,
"context-sendvideo",
true,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
]);
await SpecialPowers.popPrefEnv();
@@ -588,6 +616,10 @@ add_task(async function test_video_bad2() {
false,
"context-sendvideo",
false,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
]);
await SpecialPowers.popPrefEnv();
@@ -636,6 +668,10 @@ add_task(async function test_video_bad2() {
false,
"context-sendvideo",
false,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
]);
await SpecialPowers.popPrefEnv();
@@ -767,6 +803,10 @@ add_task(async function test_video_in_iframe() {
true,
"---",
null,
+ "context-take-frame-screenshot",
+ true,
+ "---",
+ null,
"context-viewframeinfo",
true,
]),
@@ -846,6 +886,10 @@ add_task(async function test_video_in_iframe() {
true,
"---",
null,
+ "context-take-frame-screenshot",
+ true,
+ "---",
+ null,
"context-viewframeinfo",
true,
]),
@@ -967,6 +1011,10 @@ add_task(async function test_image_in_iframe() {
true,
"---",
null,
+ "context-take-frame-screenshot",
+ true,
+ "---",
+ null,
"context-viewframeinfo",
true,
]),
@@ -1336,6 +1384,7 @@ add_task(async function test_select_text() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
"---",
null,
"context-viewpartialsource-selection",
@@ -1369,6 +1418,9 @@ add_task(async function test_select_text_search_service_not_initialized() {
null,
"context-take-screenshot",
true,
+ ...(hasSelectTranslations
+ ? ["---", null, "context-translate-selection", true]
+ : []),
"---",
null,
"context-viewpartialsource-selection",
@@ -1423,6 +1475,7 @@ add_task(async function test_select_text_link() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
"---",
null,
"context-viewpartialsource-selection",
@@ -1490,6 +1543,9 @@ add_task(async function test_imagelink() {
null,
"context-setDesktopBackground",
true,
+ ...(hasSelectTranslations
+ ? ["---", null, "context-translate-selection", true]
+ : []),
]);
});
@@ -1591,6 +1647,10 @@ add_task(async function test_longdesc() {
null,
"context-setDesktopBackground",
true,
+ "---",
+ null,
+ "context-take-screenshot",
+ true,
]);
});
@@ -1682,6 +1742,7 @@ add_task(async function test_svg_link() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
await test_contextmenu("#svg-with-link2 > a", [
@@ -1711,6 +1772,7 @@ add_task(async function test_svg_link() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
await test_contextmenu("#svg-with-link3 > a", [
@@ -1740,6 +1802,7 @@ add_task(async function test_svg_link() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
});
@@ -1771,6 +1834,7 @@ add_task(async function test_svg_relative_link() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
await test_contextmenu("#svg-with-relative-link2 > a", [
@@ -1800,6 +1864,7 @@ add_task(async function test_svg_relative_link() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
await test_contextmenu("#svg-with-relative-link3 > a", [
@@ -1829,6 +1894,7 @@ add_task(async function test_svg_relative_link() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
});
@@ -1898,6 +1964,7 @@ add_task(async function test_background_image() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
// Don't show image related context menu commands when there is a selection
@@ -1921,6 +1988,7 @@ add_task(async function test_background_image() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
"---",
null,
"context-viewpartialsource-selection",
@@ -1989,6 +2057,7 @@ add_task(async function test_strip_on_share_on_secure_about_page() {
true,
"context-searchselect-private",
true,
+ ...(hasSelectTranslations ? ["context-translate-selection", true] : []),
]);
// Clean up
diff --git a/browser/base/content/test/contextMenu/browser_contextmenu_cross_boundary_selection.js b/browser/base/content/test/contextMenu/browser_contextmenu_cross_boundary_selection.js
new file mode 100644
index 0000000000..3137a1e136
--- /dev/null
+++ b/browser/base/content/test/contextMenu/browser_contextmenu_cross_boundary_selection.js
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const PAGE = `
+ data:text/html,
+ <div>OuterText<div>
+ <div id="host">
+ <template shadowrootmode="open">
+ <span id="innerText">InnerText</span>
+ </template>
+ </div>
+ `;
+/**
+ * Tests that right click on a cross boundary selection shows the context menu
+ */
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.shadowdom.selection_across_boundary.enabled", true]],
+ });
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: PAGE,
+ },
+ async function (browser) {
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ let awaitPopupShown = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+
+ let awaitPopupHidden = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+
+ await SpecialPowers.spawn(browser, [], () => {
+ let host = content.document.getElementById("host");
+ content.getSelection().setBaseAndExtent(
+ content.document.body,
+ 0,
+ host.shadowRoot.getElementById("innerText").firstChild,
+ 3 // Only select the first three characters of the inner text
+ );
+ });
+
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "div", // Click on the div for OuterText
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ browser
+ );
+
+ await awaitPopupShown;
+
+ const allVisibleMenuItems = Array.from(contextMenu.children)
+ .filter(elem => {
+ return !elem.hidden;
+ })
+ .map(elem => elem.id);
+
+ ok(
+ allVisibleMenuItems.includes("context-copy"),
+ "copy button should exist"
+ );
+
+ contextMenu.hidePopup();
+ await awaitPopupHidden;
+ }
+ );
+});
diff --git a/browser/base/content/test/contextMenu/browser_strip_on_share_link.js b/browser/base/content/test/contextMenu/browser_strip_on_share_link.js
index 435b1aa0ff..144076cfb2 100644
--- a/browser/base/content/test/contextMenu/browser_strip_on_share_link.js
+++ b/browser/base/content/test/contextMenu/browser_strip_on_share_link.js
@@ -97,6 +97,18 @@ add_task(async function testQueryParamIsNotStrippedForWrongSiteSpecific() {
});
});
+// Ensuring clean copy works with magnet links. We don't strip anything but copying the original URI should still work.
+add_task(async function testMagneticLinks() {
+ let originalUrl = "magnet:?xt=urn:btih:somesha1hash";
+ let shortenedUrl = "magnet:?xt=urn:btih:somesha1hash";
+ await testStripOnShare({
+ selectWholeUrl: true,
+ validUrl: originalUrl,
+ strippedUrl: shortenedUrl,
+ useTestList: true,
+ });
+});
+
/**
* Opens a new tab, opens the context menu and checks that the strip-on-share menu item is visible.
* Checks that the stripped version of the url is copied to the clipboard.
diff --git a/browser/base/content/test/general/browser_documentnavigation.js b/browser/base/content/test/general/browser_documentnavigation.js
index 880db6110f..b68e6ec3f0 100644
--- a/browser/base/content/test/general/browser_documentnavigation.js
+++ b/browser/base/content/test/general/browser_documentnavigation.js
@@ -229,7 +229,7 @@ add_task(async function () {
let sidebar = document.getElementById("sidebar");
let loadPromise = BrowserTestUtils.waitForEvent(sidebar, "load", true);
- SidebarUI.toggle("viewBookmarksSidebar");
+ SidebarController.toggle("viewBookmarksSidebar");
await loadPromise;
gURLBar.focus();
@@ -278,7 +278,7 @@ add_task(async function () {
"back focus with sidebar urlbar"
);
- SidebarUI.toggle("viewBookmarksSidebar");
+ SidebarController.toggle("viewBookmarksSidebar");
});
// Navigate when the downloads panel is open
diff --git a/browser/base/content/test/performance/browser_preferences_usage.js b/browser/base/content/test/performance/browser_preferences_usage.js
index b0ff140d19..9ad9a8dde8 100644
--- a/browser/base/content/test/performance/browser_preferences_usage.js
+++ b/browser/base/content/test/performance/browser_preferences_usage.js
@@ -70,13 +70,6 @@ function checkPrefGetters(stats, max, knownProblematicPrefs = {}) {
}
}
- // This pref will be accessed by mozJSComponentLoader when loading modules,
- // which fails TV runs since they run the test multiple times without restarting.
- // We just ignore this pref, since it's for testing only anyway.
- if (knownProblematicPrefs["browser.startup.record"]) {
- delete knownProblematicPrefs["browser.startup.record"];
- }
-
let unusedPrefs = Object.keys(knownProblematicPrefs);
is(
unusedPrefs.length,
@@ -104,18 +97,9 @@ add_task(async function startup() {
let max = 40;
let knownProblematicPrefs = {
- "browser.startup.record": {
- // This pref is accessed in Nighly and debug builds only.
- min: 200,
- max: 450,
- },
"network.loadinfo.skip_type_assertion": {
// This is accessed in debug only.
},
- "chrome.override_package.global": {
- min: 0,
- max: 50,
- },
};
let startupRecorder =
@@ -135,9 +119,6 @@ add_task(async function open_10_tabs() {
const max = 4 * DEFAULT_PROCESS_COUNT;
let knownProblematicPrefs = {
- "browser.startup.record": {
- max: 20,
- },
"browser.tabs.remote.logSwitchTiming": {
max: 35,
},
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 140c2be2fd..3c652b26a2 100644
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_security_delay.js
@@ -44,22 +44,22 @@ add_setup(async function () {
});
});
+/**
+ * The security delay calculation in PopupNotification.sys.mjs is dependent on
+ * the monotonically increasing value of performance.now. This timestamp is
+ * not relative to a fixed date, but to runtime.
+ * We need to wait for the value performance.now() to be larger than the
+ * security delay in order to observe the bug. Only then does the
+ * timeSinceShown check in PopupNotifications.sys.mjs lead to a timeSinceShown
+ * value that is unconditionally greater than lazy.buttonDelay for
+ * notification.timeShown = null = 0.
+ * See: https://searchfox.org/mozilla-central/rev/f32d5f3949a3f4f185122142b29f2e3ab776836e/toolkit/modules/PopupNotifications.sys.mjs#1870-1872
+ *
+ * When running in automation as part of a larger test suite performance.now()
+ * should usually be already sufficiently high in which case this check should
+ * directly resolve.
+ */
async function ensureSecurityDelayReady() {
- /**
- * The security delay calculation in PopupNotification.sys.mjs is dependent on
- * the monotonically increasing value of performance.now. This timestamp is
- * not relative to a fixed date, but to runtime.
- * We need to wait for the value performance.now() to be larger than the
- * security delay in order to observe the bug. Only then does the
- * timeSinceShown check in PopupNotifications.sys.mjs lead to a timeSinceShown
- * value that is unconditionally greater than lazy.buttonDelay for
- * notification.timeShown = null = 0.
- * See: https://searchfox.org/mozilla-central/rev/f32d5f3949a3f4f185122142b29f2e3ab776836e/toolkit/modules/PopupNotifications.sys.mjs#1870-1872
- *
- * When running in automation as part of a larger test suite performance.now()
- * should usually be already sufficiently high in which case this check should
- * directly resolve.
- */
await TestUtils.waitForCondition(
() => performance.now() > TEST_SECURITY_DELAY,
"Wait for performance.now() > SECURITY_DELAY",
@@ -69,73 +69,73 @@ async function ensureSecurityDelayReady() {
}
/**
- * Tests that when we show a second notification while the panel is open the
- * timeShown attribute is correctly set and the security delay is enforced
- * properly.
+ * Test helper for security delay tests which performs the following steps:
+ * 1. Shows a test notification.
+ * 2. Waits for the notification panel to be shown and checks that the initial
+ * security delay blocks clicks.
+ * 3. Waits for the security delay to expire. Clicks should now be allowed.
+ * 4. Executes the provided onSecurityDelayExpired function. This function
+ * should renew the security delay.
+ * 5. Tests that the security delay was renewed.
+ * 6. Ensures that after the security delay the notification can be closed.
+ *
+ * @param {*} options
+ * @param {function} options.onSecurityDelayExpired - Function to run after the
+ * security delay has expired. This function should trigger a renew of the
+ * security delay.
+ * @param {function} options.cleanupFn - Optional cleanup function to run after
+ * the test has completed.
+ * @returns {Promise<void>} - Resolves when the test has completed.
*/
-add_task(async function test_timeShownMultipleNotifications() {
+async function runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired,
+ cleanupFn = () => {},
+}) {
await ensureSecurityDelayReady();
- ok(
- !PopupNotifications.isPanelOpen,
- "PopupNotification panel should not be open initially."
- );
-
- info("Open the first notification.");
+ info("Open a notification.");
let popupShownPromise = waitForNotificationPanel();
showNotification();
await popupShownPromise;
ok(
PopupNotifications.isPanelOpen,
- "PopupNotification should be open after first show call."
+ "PopupNotification should be open after show call."
);
- is(
- PopupNotifications._currentNotifications.length,
- 1,
- "There should only be one notification"
+ // Test that the initial security delay works.
+ info(
+ "Trigger main action via button click during the initial security delay."
);
+ triggerMainCommand(PopupNotifications.panel);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ ok(PopupNotifications.isPanelOpen, "PopupNotification should still be open.");
let notification = PopupNotifications.getNotification(
"foo",
gBrowser.selectedBrowser
);
- is(notification?.id, "foo", "There should be a notification with id foo");
- ok(notification.timeShown, "The notification should have timeShown set");
-
- info(
- "Call show again with the same notification id while the PopupNotification panel is still open."
- );
- showNotification();
ok(
- PopupNotifications.isPanelOpen,
- "PopupNotification should still open after second show call."
- );
- notification = PopupNotifications.getNotification(
- "foo",
- gBrowser.selectedBrowser
- );
- is(
- PopupNotifications._currentNotifications.length,
- 1,
- "There should still only be one notification"
+ notification,
+ "Notification should still be open because we clicked during the security delay."
);
+ // If the notification is no longer shown (test failure) skip the remaining
+ // checks.
+ if (!notification) {
+ await cleanupFn();
+ return;
+ }
- is(
- notification?.id,
- "foo",
- "There should still be a notification with id foo"
+ info("Wait for security delay to expire.");
+ await new Promise(resolve =>
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(resolve, TEST_SECURITY_DELAY + 500)
);
- ok(notification.timeShown, "The notification should have timeShown set");
- let notificationHiddenPromise = waitForNotificationPanelHidden();
-
- info("Trigger main action via button click during security delay");
-
- // Wait for a tick of the event loop to ensure the button we're clicking
- // has been slotted into moz-button-group
- await new Promise(resolve => setTimeout(resolve, 0));
+ info("Run test specific actions which restarts the security delay.");
+ await onSecurityDelayExpired();
+ info("Trigger main action via button click during the new security delay.");
triggerMainCommand(PopupNotifications.panel);
await new Promise(resolve => setTimeout(resolve, 0));
@@ -149,10 +149,10 @@ add_task(async function test_timeShownMultipleNotifications() {
notification,
"Notification should still be open because we clicked during the security delay."
);
-
// If the notification is no longer shown (test failure) skip the remaining
// checks.
if (!notification) {
+ await cleanupFn();
return;
}
@@ -163,6 +163,7 @@ add_task(async function test_timeShownMultipleNotifications() {
notification.timeShown = performance.now() - fakeTimeShown;
info("Trigger main action via button click outside security delay");
+ let notificationHiddenPromise = waitForNotificationPanelHidden();
triggerMainCommand(PopupNotifications.panel);
info("Wait for panel to be hidden.");
@@ -170,15 +171,19 @@ add_task(async function test_timeShownMultipleNotifications() {
ok(
!PopupNotifications.getNotification("foo", gBrowser.selectedBrowser),
- "Should not longer see the notification."
+ "Should no longer see the notification."
);
-});
+
+ info("Cleanup.");
+ await cleanupFn();
+}
/**
- * Tests that when we reshow a notification after a tab switch the timeShown
- * attribute is correctly reset and the security delay is enforced.
+ * Tests that when we show a second notification while the panel is open the
+ * timeShown attribute is correctly set and the security delay is enforced
+ * properly.
*/
-add_task(async function test_notificationReshowTabSwitch() {
+add_task(async function test_timeShownMultipleNotifications() {
await ensureSecurityDelayReady();
ok(
@@ -195,6 +200,12 @@ add_task(async function test_notificationReshowTabSwitch() {
"PopupNotification should be open after first show call."
);
+ is(
+ PopupNotifications._currentNotifications.length,
+ 1,
+ "There should only be one notification"
+ );
+
let notification = PopupNotifications.getNotification(
"foo",
gBrowser.selectedBrowser
@@ -202,69 +213,39 @@ add_task(async function test_notificationReshowTabSwitch() {
is(notification?.id, "foo", "There should be a notification with id foo");
ok(notification.timeShown, "The notification should have timeShown set");
- info("Trigger main action via button click during 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."
- );
-
- // If the notification is no longer shown (test failure) skip the remaining
- // checks.
- if (!notification) {
- return;
- }
-
- let panelHiddenPromise = waitForNotificationPanelHidden();
- let panelShownPromise;
-
- info("Open a new tab which hides the notification panel.");
- await BrowserTestUtils.withNewTab("https://example.com", async () => {
- info("Wait for panel to be hidden by tab switch.");
- await panelHiddenPromise;
- info(
- "Keep the tab open until the security delay for the original notification show has expired."
- );
- await new Promise(resolve =>
- // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
- setTimeout(resolve, TEST_SECURITY_DELAY + 500)
- );
-
- panelShownPromise = waitForNotificationPanel();
- });
info(
- "Wait for the panel to show again after the tab close. We're showing the original tab again."
+ "Call show again with the same notification id while the PopupNotification panel is still open."
);
- await panelShownPromise;
-
+ showNotification();
ok(
PopupNotifications.isPanelOpen,
- "PopupNotification should be shown after tab close."
+ "PopupNotification should still open after second show call."
);
notification = PopupNotifications.getNotification(
"foo",
gBrowser.selectedBrowser
);
is(
+ PopupNotifications._currentNotifications.length,
+ 1,
+ "There should still only be one notification"
+ );
+
+ is(
notification?.id,
"foo",
"There should still be a notification with id foo"
);
+ ok(notification.timeShown, "The notification should have timeShown set");
let notificationHiddenPromise = waitForNotificationPanelHidden();
- info(
- "Because we re-show the panel after tab close / switch the security delay should have reset."
- );
- info("Trigger main action via button click during the new security delay.");
+ info("Trigger main action via button click during security delay");
+
+ // Wait for a tick of the event loop to ensure the button we're clicking
+ // has been slotted into moz-button-group
+ await new Promise(resolve => setTimeout(resolve, 0));
+
triggerMainCommand(PopupNotifications.panel);
await new Promise(resolve => setTimeout(resolve, 0));
@@ -278,6 +259,7 @@ add_task(async function test_notificationReshowTabSwitch() {
notification,
"Notification should still be open because we clicked during the security delay."
);
+
// If the notification is no longer shown (test failure) skip the remaining
// checks.
if (!notification) {
@@ -298,109 +280,83 @@ add_task(async function test_notificationReshowTabSwitch() {
ok(
!PopupNotifications.getNotification("foo", gBrowser.selectedBrowser),
- "Should not longer see the notification."
+ "Should no longer see the notification."
);
});
/**
+ * Tests that when we reshow a notification after a tab switch the timeShown
+ * attribute is correctly reset and the security delay is enforced.
+ */
+add_task(async function test_notificationReshowTabSwitch() {
+ await runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired: async () => {
+ let panelHiddenPromise = waitForNotificationPanelHidden();
+ let panelShownPromise;
+
+ info("Open a new tab which hides the notification panel.");
+ await BrowserTestUtils.withNewTab("https://example.com", async () => {
+ info("Wait for panel to be hidden by tab switch.");
+ await panelHiddenPromise;
+ panelShownPromise = waitForNotificationPanel();
+ });
+ info(
+ "Wait for the panel to show again after the tab close. We're showing the original tab again."
+ );
+ await panelShownPromise;
+
+ ok(
+ PopupNotifications.isPanelOpen,
+ "PopupNotification should be shown after tab close."
+ );
+ let notification = PopupNotifications.getNotification(
+ "foo",
+ gBrowser.selectedBrowser
+ );
+ is(
+ notification?.id,
+ "foo",
+ "There should still be a notification with id foo"
+ );
+
+ info(
+ "Because we re-show the panel after tab close / switch the security delay should have reset."
+ );
+ },
+ });
+});
+
+/**
* Tests that the security delay gets reset when a window is repositioned and
* the PopupNotifications panel position is updated.
*/
add_task(async function test_notificationWindowMove() {
- await ensureSecurityDelayReady();
-
- info("Open a notification.");
- let popupShownPromise = waitForNotificationPanel();
- showNotification();
- await popupShownPromise;
- ok(
- PopupNotifications.isPanelOpen,
- "PopupNotification should be open after show call."
- );
-
- // Test that the initial security delay works.
- info("Trigger main action via button click during the new security delay.");
- triggerMainCommand(PopupNotifications.panel);
-
- await new Promise(resolve => setTimeout(resolve, 0));
-
- ok(PopupNotifications.isPanelOpen, "PopupNotification should still be open.");
- let notification = PopupNotifications.getNotification(
- "foo",
- gBrowser.selectedBrowser
- );
- ok(
- notification,
- "Notification should still be open because we clicked during the security delay."
- );
- // If the notification is no longer shown (test failure) skip the remaining
- // checks.
- if (!notification) {
- return;
- }
-
- info("Wait for security delay to expire.");
- await new Promise(resolve =>
- // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
- setTimeout(resolve, TEST_SECURITY_DELAY + 500)
- );
-
- info("Reposition the window");
- // Remember original window position.
- let { screenX, screenY } = window;
-
- let promisePopupPositioned = BrowserTestUtils.waitForEvent(
- PopupNotifications.panel,
- "popuppositioned"
- );
-
- // Move the window.
- window.moveTo(200, 200);
-
- // Wait for the panel to reposition and the PopupNotifications listener to run.
- await promisePopupPositioned;
- await new Promise(resolve => setTimeout(resolve, 0));
-
- info("Trigger main action via button click during the new 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."
- );
- // If the notification is no longer shown (test failure) skip the remaining
- // checks.
- if (!notification) {
- return;
- }
-
- // Ensure that once the security delay has passed the notification can be
- // closed again.
- let fakeTimeShown = TEST_SECURITY_DELAY + 500;
- info(`Manually set timeShown to ${fakeTimeShown}ms in the past.`);
- notification.timeShown = performance.now() - fakeTimeShown;
-
- info("Trigger main action via button click outside security delay");
- let notificationHiddenPromise = waitForNotificationPanelHidden();
- triggerMainCommand(PopupNotifications.panel);
-
- info("Wait for panel to be hidden.");
- await notificationHiddenPromise;
-
- ok(
- !PopupNotifications.getNotification("foo", gBrowser.selectedBrowser),
- "Should not longer see the notification."
- );
-
- // Reset window position
- window.moveTo(screenX, screenY);
+ let screenX, screenY;
+
+ await runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired: async () => {
+ info("Reposition the window");
+ // Remember original window position.
+ screenX = window.screenX;
+ screenY = window.screenY;
+
+ let promisePopupPositioned = BrowserTestUtils.waitForEvent(
+ PopupNotifications.panel,
+ "popuppositioned"
+ );
+
+ // Move the window.
+ window.moveTo(200, 200);
+
+ // Wait for the panel to reposition and the PopupNotifications listener to run.
+ await promisePopupPositioned;
+ await new Promise(resolve => setTimeout(resolve, 0));
+ },
+ cleanupFn: async () => {
+ // Reset window position
+ window.moveTo(screenX, screenY);
+ },
+ });
});
/**
@@ -563,5 +519,49 @@ add_task(async function test_notificationDuringFullScreenTransition() {
info("Wait for full screen transition end.");
await promiseFullScreenTransitionEnd;
info("Full screen transition end");
+
+ await SpecialPowers.popPrefEnv();
+ });
+});
+
+/**
+ * Tests that the security delay gets extended when pointer lock is entered.
+ */
+add_task(async function test_notificationPointerLock() {
+ // We need a tab to enter pointer lock.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com"
+ );
+
+ await runPopupNotificationSecurityDelayTest({
+ onSecurityDelayExpired: async () => {
+ info("Enter pointer lock");
+ // Move focus to the browser to ensure pointer lock request succeeds.
+ gBrowser.selectedBrowser.focus();
+ let pointerLockEnterPromise = TestUtils.topicObserved(
+ "pointer-lock-entered"
+ );
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+ await content.document.body.requestPointerLock();
+ });
+
+ // Wait for pointer lock to be entered and the PopupNotifications listener to run.
+ await pointerLockEnterPromise;
+ await new Promise(resolve => setTimeout(resolve, 0));
+ },
+ cleanupFn: async () => {
+ // Cleanup.
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+ await content.document.exitPointerLock();
+ });
+ await TestUtils.waitForCondition(
+ () => !window.PointerLock.isActive,
+ "Wait for pointer lock exit."
+ );
+ BrowserTestUtils.removeTab(tab);
+ },
});
});
diff --git a/browser/base/content/test/sidebar/browser_sidebar_adopt.js b/browser/base/content/test/sidebar/browser_sidebar_adopt.js
index 344a71cb9b..988ac1487f 100644
--- a/browser/base/content/test/sidebar/browser_sidebar_adopt.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_adopt.js
@@ -5,7 +5,7 @@
* during the initial browser startup - but it would be hard to do with a mochitest. */
registerCleanupFunction(() => {
- SidebarUI.hide();
+ SidebarController.hide();
});
function failIfSidebarFocusedFires() {
@@ -26,7 +26,7 @@ add_task(async function testAdoptedTwoWindows() {
info("Ensure that sidebar state is adopted only from the opener");
let win1 = await BrowserTestUtils.openNewBrowserWindow();
- await win1.SidebarUI.show("viewBookmarksSidebar");
+ await win1.SidebarController.show("viewBookmarksSidebar");
await BrowserTestUtils.closeWindow(win1);
let win2 = await BrowserTestUtils.openNewBrowserWindow();
@@ -34,7 +34,7 @@ add_task(async function testAdoptedTwoWindows() {
!win2.document.getElementById("sidebar-button").hasAttribute("checked"),
"Sidebar button isn't checked"
);
- ok(!win2.SidebarUI.isOpen, "Sidebar is closed");
+ ok(!win2.SidebarController.isOpen, "Sidebar is closed");
await BrowserTestUtils.closeWindow(win2);
});
@@ -46,7 +46,7 @@ add_task(async function testEventsReceivedInMainWindow() {
let initialShown = BrowserTestUtils.waitForEvent(window, "SidebarShown");
let initialFocus = BrowserTestUtils.waitForEvent(window, "SidebarFocused");
- await SidebarUI.show("viewBookmarksSidebar");
+ await SidebarController.show("viewBookmarksSidebar");
await initialShown;
await initialFocus;
diff --git a/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js b/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js
index 5b07da9839..1e52895a7d 100644
--- a/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_app_locale_changed.js
@@ -8,7 +8,7 @@
add_task(function cleanup() {
registerCleanupFunction(() => {
- SidebarUI.hide();
+ SidebarController.hide();
});
});
@@ -17,7 +17,7 @@ add_task(function cleanup() {
*/
async function testLiveReloading(sidebarName) {
info("Showing the sidebar " + sidebarName);
- await SidebarUI.show(sidebarName);
+ await SidebarController.show(sidebarName);
function getTreeChildren() {
const sidebarDoc =
@@ -44,7 +44,7 @@ async function testLiveReloading(sidebarName) {
);
info("Hiding the sidebar");
- SidebarUI.hide();
+ SidebarController.hide();
}
add_task(async function test_bookmarks_sidebar() {
diff --git a/browser/base/content/test/sidebar/browser_sidebar_keys.js b/browser/base/content/test/sidebar/browser_sidebar_keys.js
index f12d1cf5f7..61e4ce9737 100644
--- a/browser/base/content/test/sidebar/browser_sidebar_keys.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_keys.js
@@ -11,11 +11,11 @@ async function testSidebarKeyToggle(key, options, expectedSidebarId) {
expectedSidebarId
);
EventUtils.synthesizeKey(key, options);
- Assert.ok(!SidebarUI.isOpen);
+ Assert.ok(!SidebarController.isOpen);
}
add_task(async function test_sidebar_keys() {
- registerCleanupFunction(() => SidebarUI.hide());
+ registerCleanupFunction(() => SidebarController.hide());
await testSidebarKeyToggle("b", { accelKey: true }, "viewBookmarksSidebar");
@@ -30,7 +30,7 @@ add_task(async function test_sidebar_in_customize_mode() {
let { CustomizableUI } = ChromeUtils.importESModule(
"resource:///modules/CustomizableUI.sys.mjs"
);
- registerCleanupFunction(() => SidebarUI.hide());
+ registerCleanupFunction(() => SidebarController.hide());
let placement = CustomizableUI.getPlacementOfWidget("sidebar-button");
if (!(placement?.area == CustomizableUI.AREA_NAVBAR)) {
@@ -55,7 +55,7 @@ add_task(async function test_sidebar_in_customize_mode() {
).a;
let promiseShown = BrowserTestUtils.waitForEvent(window, "SidebarShown");
- SidebarUI.show("viewBookmarksSidebar");
+ SidebarController.show("viewBookmarksSidebar");
await promiseShown;
Assert.greater(
@@ -80,8 +80,8 @@ add_task(async function test_sidebar_in_customize_mode() {
);
// Attempt toggle - should fail in customize mode.
- await SidebarUI.toggle();
- ok(SidebarUI.isOpen, "Sidebar is still open");
+ await SidebarController.toggle();
+ ok(SidebarController.isOpen, "Sidebar is still open");
// Exit customize mode. This should re-enable the toggle and make the sidebar
// toggle widget appear checked again, since toggle() didn't hide the sidebar.
@@ -98,8 +98,8 @@ add_task(async function test_sidebar_in_customize_mode() {
"Sidebar widget background should appear checked again"
);
- await SidebarUI.toggle();
- ok(!SidebarUI.isOpen, "Sidebar is closed");
+ await SidebarController.toggle();
+ ok(!SidebarController.isOpen, "Sidebar is closed");
Assert.equal(
getBGAlpha(),
0,
diff --git a/browser/base/content/test/sidebar/browser_sidebar_move.js b/browser/base/content/test/sidebar/browser_sidebar_move.js
index d434b3bbd8..05ea9e3322 100644
--- a/browser/base/content/test/sidebar/browser_sidebar_move.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_move.js
@@ -1,17 +1,17 @@
registerCleanupFunction(() => {
Services.prefs.clearUserPref("sidebar.position_start");
- SidebarUI.hide();
+ SidebarController.hide();
});
const EXPECTED_START_ORDINALS = [
- ["sidebar-launcher", 1],
+ ["sidebar-main", 1],
["sidebar-box", 2],
["sidebar-splitter", 3],
["appcontent", 4],
];
const EXPECTED_END_ORDINALS = [
- ["sidebar-launcher", 5],
+ ["sidebar-main", 5],
["sidebar-box", 4],
["sidebar-splitter", 3],
["appcontent", 2],
@@ -25,8 +25,8 @@ function getBrowserChildrenWithOrdinals() {
}
add_task(async function () {
- await SidebarUI.show("viewBookmarksSidebar");
- SidebarUI.showSwitcherPanel();
+ await SidebarController.show("viewBookmarksSidebar");
+ SidebarController.showSwitcherPanel();
let reversePositionButton = document.getElementById(
"sidebar-reverse-position"
@@ -43,8 +43,8 @@ add_task(async function () {
ok(!box.hasAttribute("positionend"), "Positioned start");
// Moved to right
- SidebarUI.reversePosition();
- SidebarUI.showSwitcherPanel();
+ SidebarController.reversePosition();
+ SidebarController.showSwitcherPanel();
Assert.deepEqual(
getBrowserChildrenWithOrdinals(),
EXPECTED_END_ORDINALS,
@@ -58,8 +58,8 @@ add_task(async function () {
ok(box.hasAttribute("positionend"), "Positioned end");
// Moved to back to left
- SidebarUI.reversePosition();
- SidebarUI.showSwitcherPanel();
+ SidebarController.reversePosition();
+ SidebarController.showSwitcherPanel();
Assert.deepEqual(
getBrowserChildrenWithOrdinals(),
EXPECTED_START_ORDINALS,
diff --git a/browser/base/content/test/sidebar/browser_sidebar_persist.js b/browser/base/content/test/sidebar/browser_sidebar_persist.js
index fe67bed9e0..4977c7ef0f 100644
--- a/browser/base/content/test/sidebar/browser_sidebar_persist.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_persist.js
@@ -18,7 +18,7 @@ add_task(async function persist_sidebar_width() {
{
info("Showing new window and setting sidebar box");
const win = await BrowserTestUtils.openNewBrowserWindow();
- await win.SidebarUI.show("viewBookmarksSidebar");
+ await win.SidebarController.show("viewBookmarksSidebar");
win.document.getElementById("sidebar-box").style.width = "100px";
await BrowserTestUtils.closeWindow(win);
}
@@ -26,7 +26,7 @@ add_task(async function persist_sidebar_width() {
{
info("Showing new window and seeing persisted width");
const win = await BrowserTestUtils.openNewBrowserWindow();
- await win.SidebarUI.show("viewBookmarksSidebar");
+ await win.SidebarController.show("viewBookmarksSidebar");
is(
win.document.getElementById("sidebar-box").style.width,
"100px",
diff --git a/browser/base/content/test/sidebar/browser_sidebar_switcher.js b/browser/base/content/test/sidebar/browser_sidebar_switcher.js
index 032c23b029..0fc9e18e01 100644
--- a/browser/base/content/test/sidebar/browser_sidebar_switcher.js
+++ b/browser/base/content/test/sidebar/browser_sidebar_switcher.js
@@ -1,5 +1,5 @@
registerCleanupFunction(() => {
- SidebarUI.hide();
+ SidebarController.hide();
});
/**
@@ -9,14 +9,14 @@ registerCleanupFunction(() => {
*/
function showSwitcherPanelPromise() {
return new Promise(resolve => {
- SidebarUI._switcherPanel.addEventListener(
+ SidebarController._switcherPanel.addEventListener(
"popupshown",
() => {
resolve();
},
{ once: true }
);
- SidebarUI.showSwitcherPanel();
+ SidebarController.showSwitcherPanel();
});
}
@@ -25,7 +25,10 @@ function showSwitcherPanelPromise() {
* @returns Promise which resolves when the popup menu is opened
*/
async function waitForSwitcherPopupShown() {
- return BrowserTestUtils.waitForEvent(SidebarUI._switcherPanel, "popupshown");
+ return BrowserTestUtils.waitForEvent(
+ SidebarController._switcherPanel,
+ "popupshown"
+ );
}
/**
@@ -63,7 +66,7 @@ async function testSidebarMenuKeyToggle(key, sidebarTitle) {
info(`Testing "${key}" key handling of sidebar menu popup items
to access ${sidebarTitle} sidebar`);
- Assert.ok(SidebarUI.isOpen, "Sidebar is open");
+ Assert.ok(SidebarController.isOpen, "Sidebar is open");
let sidebarSwitcher = document.querySelector("#sidebar-switcher-target");
let sidebar = document.getElementById("sidebar");
@@ -89,7 +92,7 @@ async function testSidebarMenuKeyToggle(key, sidebarTitle) {
"The sidebar switcher target button is focused"
);
Assert.equal(
- SidebarUI._switcherPanel.state,
+ SidebarController._switcherPanel.state,
"closed",
"Sidebar menu popup is closed"
);
@@ -102,7 +105,7 @@ async function testSidebarMenuKeyToggle(key, sidebarTitle) {
await promisePopupShown;
Assert.equal(
- SidebarUI._switcherPanel.state,
+ SidebarController._switcherPanel.state,
"open",
"Sidebar menu popup is open"
);
@@ -111,7 +114,7 @@ async function testSidebarMenuKeyToggle(key, sidebarTitle) {
let arrowDown = async (menuitemId, msg) => {
let menuItemActive = BrowserTestUtils.waitForEvent(
- SidebarUI._switcherPanel,
+ SidebarController._switcherPanel,
"DOMMenuItemActive"
);
EventUtils.synthesizeKey("KEY_ArrowDown", {});
@@ -149,18 +152,18 @@ async function testSidebarMenuKeyToggle(key, sidebarTitle) {
info("Testing keyboard navigation when a sidebar menu popup is closed");
Assert.equal(
- SidebarUI._switcherPanel.state,
+ SidebarController._switcherPanel.state,
"closed",
"Sidebar menu popup is closed"
);
// Test the sidebar panel is updated
Assert.equal(
- SidebarUI._box.getAttribute("sidebarcommand"),
+ SidebarController._box.getAttribute("sidebarcommand"),
`view${sidebarTitle}Sidebar` /* e.g. "viewHistorySidebar" */,
`${sidebarTitle} sidebar loaded`
);
Assert.equal(
- SidebarUI.currentID,
+ SidebarController.currentID,
`view${sidebarTitle}Sidebar` /* e.g. "viewHistorySidebar" */,
`${sidebarTitle}'s current ID is updated to a target view`
);
@@ -173,7 +176,7 @@ add_task(async function markup() {
false,
"Unexpected sidebar found - a previous test failed to cleanup correctly"
);
- SidebarUI.hide();
+ SidebarController.hide();
}
let sidebarPopup = document.querySelector("#sidebarMenu-popup");
@@ -205,7 +208,7 @@ add_task(async function markup() {
info("Test dynamic changes in the markup of the sidebar switcher control");
- await SidebarUI.show("viewBookmarksSidebar");
+ await SidebarController.show("viewBookmarksSidebar");
await showSwitcherPanelPromise();
Assert.equal(
@@ -229,25 +232,25 @@ add_task(async function markup() {
"Sidebar switcher button is collapsed when a sidebar menu is dismissed"
);
- SidebarUI.hide();
+ SidebarController.hide();
});
add_task(async function keynav() {
// If a sidebar is already open, close it.
- if (SidebarUI.isOpen) {
+ if (SidebarController.isOpen) {
Assert.ok(
false,
"Unexpected sidebar found - a previous test failed to cleanup correctly"
);
- SidebarUI.hide();
+ SidebarController.hide();
}
- await SidebarUI.show("viewBookmarksSidebar");
+ await SidebarController.show("viewBookmarksSidebar");
await testSidebarMenuKeyToggle("KEY_Enter", "History");
await testSidebarMenuKeyToggle(" ", "Tabs");
- SidebarUI.hide();
+ SidebarController.hide();
});
add_task(async function mouse() {
@@ -257,11 +260,11 @@ add_task(async function mouse() {
false,
"Unexpected sidebar found - a previous test failed to cleanup correctly"
);
- SidebarUI.hide();
+ SidebarController.hide();
}
let sidebar = document.querySelector("#sidebar-box");
- await SidebarUI.show("viewBookmarksSidebar");
+ await SidebarController.show("viewBookmarksSidebar");
await showSwitcherPanelPromise();
await pickSwitcherMenuitem("#sidebar-switcher-history");
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 d73c3fe8de..9668288181 100644
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -276,12 +276,6 @@ var allowlist = [
// find the references)
{ file: "chrome://browser/content/screenshots/copied-notification.svg" },
- // Bug 1875361
- { file: "chrome://global/content/ml/SummarizerModel.sys.mjs" },
-
- // Bug 1886130
- { file: "chrome://global/content/ml/ModelHub.sys.mjs" },
-
// toolkit/xre/MacRunFromDmgUtils.mm
{ file: "resource://gre/localization/en-US/toolkit/global/run-from-dmg.ftl" },
@@ -290,6 +284,12 @@ var allowlist = [
{ file: "chrome://browser/content/screenshots/copy.svg" },
{ file: "chrome://browser/content/screenshots/download.svg" },
{ file: "chrome://browser/content/screenshots/download-white.svg" },
+
+ // Referenced programmatically
+ { file: "chrome://browser/content/backup/BackupManifest.1.schema.json" },
+
+ // Bug 1892002
+ { file: "resource://app/modules/TopSites.sys.mjs" },
];
if (AppConstants.NIGHTLY_BUILD) {
@@ -301,6 +301,10 @@ if (AppConstants.NIGHTLY_BUILD) {
// A debug tool that is only available in Nightly builds, and is accessed
// directly by developers via the chrome URI (bug 1888491)
{ file: "chrome://browser/content/backup/debug.html" },
+
+ // The Transformers.js prod lib is not used in Nightly builds
+ { file: "chrome://global/content/ml/transformers.js" },
+ { file: "chrome://global/content/ml/ort.js" },
]
);
}
diff --git a/browser/base/content/test/static/browser_parsable_css.js b/browser/base/content/test/static/browser_parsable_css.js
index fb91da578a..6b075fc98f 100644
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -36,6 +36,12 @@ let ignoreList = [
errorMessage: /Unknown property.*overflow-clip-box/i,
isFromDevTools: false,
},
+ // content: -moz-alt-content is UA-only.
+ {
+ sourceName: /\b(html)\.css$/i,
+ errorMessage: /Error in parsing value for ‘content’/i,
+ isFromDevTools: false,
+ },
// These variables are declared somewhere else, and error when we load the
// files directly. They're all marked intermittent because their appearance
// in the error console seems to not be consistent.
diff --git a/browser/base/content/test/static/browser_parsable_script.js b/browser/base/content/test/static/browser_parsable_script.js
index 3d8fbc1535..71a2930ddc 100644
--- a/browser/base/content/test/static/browser_parsable_script.js
+++ b/browser/base/content/test/static/browser_parsable_script.js
@@ -19,6 +19,7 @@ const kESModuleList = new Set([
/browser\/vpn-card.js$/,
/toolkit\/content\/global\/certviewer\/components\/.*\.js$/,
/toolkit\/content\/global\/certviewer\/.*\.js$/,
+ /toolkit\/content\/global\/ml\/transformers.*\.js$/,
/chrome\/pdfjs\/content\/web\/.*\.js$/,
]);
diff --git a/browser/base/content/test/static/browser_sentence_case_strings.js b/browser/base/content/test/static/browser_sentence_case_strings.js
index e995f76b1a..12952c9600 100644
--- a/browser/base/content/test/static/browser_sentence_case_strings.js
+++ b/browser/base/content/test/static/browser_sentence_case_strings.js
@@ -103,7 +103,7 @@ function checkSubheaders(view) {
}
async function checkUpdateBanner(view) {
- let banner = view.querySelector("#appMenu-proton-update-banner");
+ let banner = view.querySelector("#appMenu-update-banner");
const notifications = [
"update-downloading",
diff --git a/browser/base/content/test/sync/browser_contextmenu_sendpage.js b/browser/base/content/test/sync/browser_contextmenu_sendpage.js
index a80cf8a1d0..14b5f72860 100644
--- a/browser/base/content/test/sync/browser_contextmenu_sendpage.js
+++ b/browser/base/content/test/sync/browser_contextmenu_sendpage.js
@@ -93,47 +93,44 @@ add_task(async function test_link_contextmenu() {
"context-sendlinktodevice-popup"
);
- let expectedArray = ["context-openlinkintab"];
-
- if (
+ const expectOpenLinkInUserContextMenu =
Services.prefs.getBoolPref("privacy.userContext.enabled") &&
- ContextualIdentityService.getPublicIdentities().length
- ) {
- expectedArray.push("context-openlinkinusercontext-menu");
- }
+ ContextualIdentityService.getPublicIdentities().length;
+
+ const expectStripOnShareLink = Services.prefs.getBoolPref(
+ "privacy.query_stripping.strip_on_share.enabled"
+ );
+
+ const expectTranslateSelection =
+ Services.prefs.getBoolPref("browser.translations.enable") &&
+ Services.prefs.getBoolPref("browser.translations.select.enable");
- expectedArray.push(
+ const expectInspectAccessibility =
+ Services.prefs.getBoolPref("devtools.accessibility.enabled", true) &&
+ (Services.prefs.getBoolPref("devtools.everOpened", false) ||
+ Services.prefs.getIntPref("devtools.selfxss.count", 0) > 0);
+
+ const expectedArray = [
+ "context-openlinkintab",
+ ...(expectOpenLinkInUserContextMenu
+ ? ["context-openlinkinusercontext-menu"]
+ : []),
"context-openlink",
"context-openlinkprivate",
"context-sep-open",
"context-bookmarklink",
"context-savelink",
"context-savelinktopocket",
- "context-copylink"
- );
-
- if (
- Services.prefs.getBoolPref("privacy.query_stripping.strip_on_share.enabled")
- ) {
- expectedArray.push("context-stripOnShareLink");
- }
-
- expectedArray.push(
+ "context-copylink",
+ ...(expectStripOnShareLink ? ["context-stripOnShareLink"] : []),
"context-sendlinktodevice",
"context-sep-sendlinktodevice",
"context-searchselect",
- "frame-sep"
- );
-
- if (
- Services.prefs.getBoolPref("devtools.accessibility.enabled", true) &&
- (Services.prefs.getBoolPref("devtools.everOpened", false) ||
- Services.prefs.getIntPref("devtools.selfxss.count", 0) > 0)
- ) {
- expectedArray.push("context-inspect-a11y");
- }
-
- expectedArray.push("context-inspect");
+ ...(expectTranslateSelection ? ["context-translate-selection"] : []),
+ "frame-sep",
+ ...(expectInspectAccessibility ? ["context-inspect-a11y"] : []),
+ "context-inspect",
+ ];
let menu = document.getElementById("contentAreaContextMenu");
diff --git a/browser/base/content/test/sync/browser_sync.js b/browser/base/content/test/sync/browser_sync.js
index 8f687842a4..03077ea67c 100644
--- a/browser/base/content/test/sync/browser_sync.js
+++ b/browser/base/content/test/sync/browser_sync.js
@@ -607,17 +607,20 @@ add_task(async function test_experiment_ui_state_unconfigured() {
checkFxAAvatar("not_configured");
let expectedLabel = gSync.fluentStrings.formatValueSync(
- "appmenuitem-sign-in-account"
+ "synced-tabs-fxa-sign-in"
+ );
+
+ let expectedDescriptionLabel = gSync.fluentStrings.formatValueSync(
+ "fxa-menu-sync-description"
);
await openMainPanel();
checkFxaToolbarButtonPanel({
headerTitle: expectedLabel,
- headerDescription: "",
+ headerDescription: expectedDescriptionLabel,
enabledItems: [
"PanelUI-fxa-cta-menu",
- "PanelUI-fxa-menu-sync-button",
"PanelUI-fxa-menu-monitor-button",
"PanelUI-fxa-menu-relay-button",
"PanelUI-fxa-menu-vpn-button",
@@ -690,7 +693,6 @@ add_task(async function test_experiment_ui_state_signedin() {
"PanelUI-fxa-menu-sync-prefs-button",
"PanelUI-fxa-menu-account-signout-button",
"PanelUI-fxa-cta-menu",
- "PanelUI-fxa-menu-sync-button",
"PanelUI-fxa-menu-monitor-button",
"PanelUI-fxa-menu-relay-button",
"PanelUI-fxa-menu-vpn-button",
diff --git a/browser/base/content/test/tabPrompts/browser.toml b/browser/base/content/test/tabPrompts/browser.toml
index 037f1f0d2b..aa7d4c724e 100644
--- a/browser/base/content/test/tabPrompts/browser.toml
+++ b/browser/base/content/test/tabPrompts/browser.toml
@@ -39,6 +39,8 @@ support-files = ["file_beforeunload_stop.html"]
https_first_disabled = true
support-files = ["openPromptOffTimeout.html"]
+["browser_promptDelays.js"]
+
["browser_promptFocus.js"]
["browser_prompt_close_event.js"]
diff --git a/browser/base/content/test/tabPrompts/browser_promptDelays.js b/browser/base/content/test/tabPrompts/browser_promptDelays.js
new file mode 100644
index 0000000000..ecd01cdb69
--- /dev/null
+++ b/browser/base/content/test/tabPrompts/browser_promptDelays.js
@@ -0,0 +1,113 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PERMISSION_DIALOG =
+ "chrome://mozapps/content/handling/permissionDialog.xhtml";
+
+add_setup(async function () {
+ // Set a new handler as default.
+ const protoSvc = Cc[
+ "@mozilla.org/uriloader/external-protocol-service;1"
+ ].getService(Ci.nsIExternalProtocolService);
+ let protoInfo = protoSvc.getProtocolHandlerInfo("web+testprotocol");
+ protoInfo.preferredAction = protoInfo.useHelperApp;
+ let handler = Cc["@mozilla.org/uriloader/web-handler-app;1"].createInstance(
+ Ci.nsIWebHandlerApp
+ );
+ handler.uriTemplate = "https://example.com/foobar?uri=%s";
+ handler.name = "Test protocol";
+ let handlers = protoInfo.possibleApplicationHandlers;
+ handlers.appendElement(handler);
+
+ protoInfo.preferredApplicationHandler = handler;
+ protoInfo.alwaysAskBeforeHandling = false;
+
+ const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
+ Ci.nsIHandlerService
+ );
+ handlerSvc.store(protoInfo);
+
+ registerCleanupFunction(() => {
+ handlerSvc.remove(protoInfo);
+ });
+});
+
+add_task(async function test_promptWhileNotForeground() {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ let windowOpened = BrowserTestUtils.waitForNewWindow();
+ await SpecialPowers.spawn(browser, [], () => {
+ content.eval(`window.open('about:blank', "_blank", "height=600");`);
+ });
+ let otherWin = await windowOpened;
+ info("Opened extra window, now start a prompt.");
+
+ // To ensure we test the delay helper correctly, shorten the delay:
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.dialog_enable_delay", 50]],
+ });
+
+ let promptPromise = BrowserTestUtils.promiseAlertDialogOpen(
+ null,
+ PERMISSION_DIALOG,
+ { isSubDialog: true }
+ );
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.location.href = "web+testprotocol:hello";
+ });
+ info("Started opening prompt.");
+ let prompt = await promptPromise;
+ info("Opened prompt.");
+ let dialog = prompt.document.querySelector("dialog");
+ let button = dialog.getButton("accept");
+ is(button.getAttribute("disabled"), "true", "Button should be disabled");
+
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 500));
+ is(
+ button.getAttribute("disabled"),
+ "true",
+ "Button should still be disabled while the dialog is in the background"
+ );
+
+ let buttonGetsEnabled = BrowserTestUtils.waitForMutationCondition(
+ button,
+ { attributeFilter: ["disabled"] },
+ () => button.getAttribute("disabled") != "true"
+ );
+ await BrowserTestUtils.closeWindow(otherWin);
+ info("Waiting for button to be enabled.");
+ await buttonGetsEnabled;
+ ok(true, "The button was enabled.");
+ dialog.cancelDialog();
+
+ await SpecialPowers.popPrefEnv();
+ });
+});
+
+add_task(async function test_promptWhileForeground() {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ let promptPromise = BrowserTestUtils.promiseAlertDialogOpen(
+ null,
+ PERMISSION_DIALOG,
+ { isSubDialog: true }
+ );
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.location.href = "web+testprotocol:hello";
+ });
+ info("Started opening prompt.");
+ let prompt = await promptPromise;
+ info("Opened prompt.");
+ let dialog = prompt.document.querySelector("dialog");
+ let button = dialog.getButton("accept");
+ is(button.getAttribute("disabled"), "true", "Button should be disabled");
+ await BrowserTestUtils.waitForMutationCondition(
+ button,
+ { attributeFilter: ["disabled"] },
+ () => button.getAttribute("disabled") != "true"
+ );
+ ok(true, "The button was enabled.");
+ dialog.cancelDialog();
+ });
+});
diff --git a/browser/base/content/test/tabPrompts/browser_promptFocus.js b/browser/base/content/test/tabPrompts/browser_promptFocus.js
index 89ca064c10..cab812f57b 100644
--- a/browser/base/content/test/tabPrompts/browser_promptFocus.js
+++ b/browser/base/content/test/tabPrompts/browser_promptFocus.js
@@ -20,8 +20,7 @@ add_task(async function test_tabdialogbox_tab_switch_focus() {
tabPromises.push(
BrowserTestUtils.openNewForegroundTab(
gBrowser,
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
- "http://example.com",
+ "https://example.com",
true
)
);
diff --git a/browser/base/content/test/tabs/browser.toml b/browser/base/content/test/tabs/browser.toml
index fa77a8b1a4..8a95c87a6e 100644
--- a/browser/base/content/test/tabs/browser.toml
+++ b/browser/base/content/test/tabs/browser.toml
@@ -30,6 +30,8 @@ skip-if = [
["browser_bfcache_exemption_about_pages.js"]
skip-if = ["!fission"]
+["browser_blank_tab_label.js"]
+
["browser_bug580956.js"]
["browser_bug_1387976_restore_lazy_tab_browser_muted_state.js"]
@@ -140,6 +142,8 @@ support-files = [
["browser_multiselect_tabs_close.js"]
+["browser_multiselect_tabs_close_duplicate_tabs.js"]
+
["browser_multiselect_tabs_close_other_tabs.js"]
["browser_multiselect_tabs_close_tabs_to_the_left.js"]
diff --git a/browser/base/content/test/tabs/browser_blank_tab_label.js b/browser/base/content/test/tabs/browser_blank_tab_label.js
new file mode 100644
index 0000000000..9fe5f6b1b0
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_blank_tab_label.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Ensure that we don't use an entirely-blank (non-printable) document title
+ * as the tab label.
+ */
+add_task(async function test_ensure_printable_label() {
+ const TEST_DOC = `
+ <!DOCTYPE html>
+ <meta charset="utf-8">
+ <!-- Title is NO-BREAK SPACE, COMBINING ACUTE ACCENT, ARABIC LETTER MARK -->
+ <title>&nbsp;&%23x0301;&%23x061C;</title>
+ Is my title blank?`;
+
+ let newTab;
+ function tabLabelChecker() {
+ Assert.stringMatches(
+ newTab.label,
+ /\p{L}|\p{N}|\p{P}|\p{S}/u,
+ "Tab label should contain printable character."
+ );
+ }
+ let mutationObserver = new MutationObserver(tabLabelChecker);
+ registerCleanupFunction(() => mutationObserver.disconnect());
+
+ gBrowser.tabContainer.addEventListener(
+ "TabOpen",
+ event => {
+ newTab = event.target;
+ tabLabelChecker();
+ mutationObserver.observe(newTab, {
+ attributeFilter: ["label"],
+ });
+ },
+ { once: true }
+ );
+
+ await BrowserTestUtils.withNewTab("data:text/html," + TEST_DOC, async () => {
+ // Wait another longer-than-tick to ensure more mutation observer things have
+ // come in.
+ await new Promise(executeSoon);
+
+ // Check one last time for good measure, for the final label:
+ tabLabelChecker();
+ });
+});
diff --git a/browser/base/content/test/tabs/browser_multiselect_tabs_close_duplicate_tabs.js b/browser/base/content/test/tabs/browser_multiselect_tabs_close_duplicate_tabs.js
new file mode 100644
index 0000000000..d18795447f
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_close_duplicate_tabs.js
@@ -0,0 +1,178 @@
+const PREF_WARN_ON_CLOSE = "browser.tabs.warnOnCloseOtherTabs";
+const PREF_SHOWN_DUPE_DIALOG =
+ "browser.tabs.haveShownCloseAllDuplicateTabsWarning";
+
+add_task(async function setPref() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_WARN_ON_CLOSE, false],
+ [PREF_SHOWN_DUPE_DIALOG, true],
+ ],
+ });
+});
+
+add_task(async function withAMultiSelectedTab() {
+ let initialTab = gBrowser.selectedTab;
+ let tab1 = await addTab();
+ let tab2 = await addTab();
+ let tab3 = await addTab();
+ let tab4 = await addTab();
+
+ is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
+
+ await triggerClickOn(tab1, { ctrlKey: true });
+
+ let tab4Pinned = BrowserTestUtils.waitForEvent(tab4, "TabPinned");
+ gBrowser.pinTab(tab4);
+ await tab4Pinned;
+
+ ok(initialTab.multiselected, "InitialTab is multiselected");
+ ok(tab1.multiselected, "Tab1 is multiselected");
+ ok(!tab2.multiselected, "Tab2 is not multiselected");
+ ok(!tab3.multiselected, "Tab3 is not multiselected");
+ ok(!tab4.multiselected, "Tab4 is not multiselected");
+ ok(tab4.pinned, "Tab4 is pinned");
+ is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
+ is(gBrowser.selectedTab, initialTab, "InitialTab is the active tab");
+
+ let closingTabs = [tab2, tab3];
+ let tabClosingPromises = [];
+ for (let tab of closingTabs) {
+ tabClosingPromises.push(BrowserTestUtils.waitForTabClosing(tab));
+ }
+
+ gBrowser.removeDuplicateTabs(tab1);
+
+ await Promise.all(tabClosingPromises);
+
+ ok(!initialTab.closing, "InitialTab is not closing");
+ ok(!tab1.closing, "Tab1 is not closing");
+ ok(tab2.closing, "Tab2 is closing");
+ ok(tab3.closing, "Tab3 is closing");
+ ok(!tab4.closing, "Tab4 is not closing");
+ is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
+ is(gBrowser.selectedTab, initialTab, "InitialTab is still the active tab");
+
+ gBrowser.clearMultiSelectedTabs();
+ BrowserTestUtils.removeTab(tab1);
+ BrowserTestUtils.removeTab(tab4);
+});
+
+add_task(async function withNotAMultiSelectedTab() {
+ let initialTab = gBrowser.selectedTab;
+ let tab1 = await addTab("http://mochi.test:8888/");
+ let tab2 = await addTab("http://mochi.test:8888/");
+ let tab3 = await addTab("http://mochi.test:8888/");
+ let tab4 = await addTab("http://mochi.test:8888/");
+ let tab5 = await addTab("http://mochi.test:8888/");
+ let tab6 = await addTab("http://mochi.test:8888/", { userContextId: 1 });
+
+ is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
+
+ await BrowserTestUtils.switchTab(gBrowser, tab1);
+ await triggerClickOn(tab2, { ctrlKey: true });
+ await triggerClickOn(tab5, { ctrlKey: true });
+
+ let tab4Pinned = BrowserTestUtils.waitForEvent(tab4, "TabPinned");
+ gBrowser.pinTab(tab4);
+ await tab4Pinned;
+
+ let tab5Pinned = BrowserTestUtils.waitForEvent(tab5, "TabPinned");
+ gBrowser.pinTab(tab5);
+ await tab5Pinned;
+
+ ok(!initialTab.multiselected, "InitialTab is not multiselected");
+ ok(tab1.multiselected, "Tab1 is multiselected");
+ ok(tab2.multiselected, "Tab2 is multiselected");
+ ok(!tab3.multiselected, "Tab3 is not multiselected");
+ ok(!tab4.multiselected, "Tab4 is not multiselected");
+ ok(tab4.pinned, "Tab4 is pinned");
+ ok(tab5.multiselected, "Tab5 is multiselected");
+ ok(tab5.pinned, "Tab5 is pinned");
+ ok(!tab6.multiselected, "Tab6 is not multiselected");
+ ok(!tab6.pinned, "Tab6 is not pinned");
+ is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs");
+ is(gBrowser.selectedTab, tab1, "Tab1 is the active tab");
+
+ let closingTabs = [tab1, tab2];
+ let tabClosingPromises = [];
+ for (let tab of closingTabs) {
+ tabClosingPromises.push(BrowserTestUtils.waitForTabClosing(tab));
+ }
+
+ await BrowserTestUtils.switchTab(
+ gBrowser,
+ gBrowser.removeDuplicateTabs(tab3)
+ );
+
+ await Promise.all(tabClosingPromises);
+
+ ok(!initialTab.closing, "InitialTab is not closing");
+ ok(tab1.closing, "Tab1 is closing");
+ ok(tab2.closing, "Tab2 is closing");
+ ok(!tab3.closing, "Tab3 is not closing");
+ ok(!tab4.closing, "Tab4 is not closing");
+ ok(!tab5.closing, "Tab5 is not closing");
+ ok(!tab6.closing, "Tab6 is not closing");
+ is(
+ gBrowser.multiSelectedTabsCount,
+ 0,
+ "Zero multiselected tabs, selection is cleared"
+ );
+ is(gBrowser.selectedTab, tab3, "tab3 is the active tab now");
+
+ for (let tab of [tab3, tab4, tab5, tab6]) {
+ BrowserTestUtils.removeTab(tab);
+ }
+});
+
+add_task(async function closeAllDuplicateTabs() {
+ let initialTab = gBrowser.selectedTab;
+ let tab1 = await addTab("http://mochi.test:8888/one");
+ let tab2 = await addTab("http://mochi.test:8888/two", { userContextId: 1 });
+ let tab3 = await addTab("http://mochi.test:8888/one");
+ let tab4 = await addTab("http://mochi.test:8888/two");
+ let tab5 = await addTab("http://mochi.test:8888/one");
+ let tab6 = await addTab("http://mochi.test:8888/two");
+
+ let tab1Pinned = BrowserTestUtils.waitForEvent(tab1, "TabPinned");
+ gBrowser.pinTab(tab1);
+ await tab1Pinned;
+
+ // So we have 1p,2c,1,2,1,2
+ // We expect 1p,2c,X,2,X,X because the pinned 1 will dupe the other two 1,
+ // but the 2c's userContextId makes it unique against the other two 2,
+ // but one of the other two 2 will close.
+
+ // Ensure tab4 remains by making it active more recently than tab6.
+ tab4._lastSeenActive = Date.now(); // as recent as it gets.
+
+ // Assert some preconditions:
+ ok(tab1.pinned, "Tab1 is pinned");
+ Assert.greater(tab4.lastSeenActive, tab6.lastSeenActive);
+
+ let closingTabs = [tab3, tab5, tab6];
+ let tabClosingPromises = [];
+ for (let tab of closingTabs) {
+ tabClosingPromises.push(BrowserTestUtils.waitForTabClosing(tab));
+ }
+
+ await BrowserTestUtils.switchTab(
+ gBrowser,
+ gBrowser.removeAllDuplicateTabs(initialTab)
+ );
+
+ await Promise.all(tabClosingPromises);
+
+ ok(!initialTab.closing, "InitialTab is not closing");
+ ok(!tab1.closing, "Tab1 is not closing");
+ ok(!tab2.closing, "Tab2 is not closing");
+ ok(tab3.closing, "Tab3 is closing");
+ ok(!tab4.closing, "Tab4 is not closing");
+ ok(tab5.closing, "Tab5 is closing");
+ ok(tab6.closing, "Tab6 is closing");
+
+ for (let tab of [tab1, tab2, 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 0f83b1e28c..19ba85b9f8 100644
--- a/browser/base/content/test/tabs/browser_tab_preview.js
+++ b/browser/base/content/test/tabs/browser_tab_preview.js
@@ -34,6 +34,7 @@ add_setup(async function () {
set: [
["browser.tabs.cardPreview.enabled", true],
["browser.tabs.cardPreview.showThumbnails", false],
+ ["browser.tabs.tooltipsShowPidAndActiveness", false],
["ui.tooltip.delay_ms", 0],
],
});
@@ -82,6 +83,99 @@ add_task(async function hoverTests() {
});
/**
+ * Verify that the pid and activeness statuses are not shown
+ * when the flag is not enabled.
+ */
+add_task(async function pidAndActivenessHiddenByDefaultTests() {
+ const tabUrl1 =
+ "data:text/html,<html><head><title>First New Tab</title></head><body>Hello</body></html>";
+ const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, tabUrl1);
+ const previewContainer = document.getElementById("tab-preview-panel");
+
+ await openPreview(tab1);
+ Assert.equal(
+ previewContainer.querySelector(".tab-preview-pid").innerText,
+ "",
+ "Tab PID is not shown"
+ );
+ Assert.equal(
+ previewContainer.querySelector(".tab-preview-activeness").innerText,
+ "",
+ "Tab activeness is not shown"
+ );
+
+ await closePreviews();
+
+ BrowserTestUtils.removeTab(tab1);
+
+ // Move the mouse outside of the tab strip.
+ EventUtils.synthesizeMouseAtCenter(document.documentElement, {
+ type: "mouseover",
+ });
+});
+
+add_task(async function pidAndActivenessTests() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.tabs.tooltipsShowPidAndActiveness", true]],
+ });
+
+ const tabUrl1 =
+ "data:text/html,<html><head><title>Single process tab</title></head><body>Hello</body></html>";
+ const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, tabUrl1);
+ const tabUrl2 = `data:text/html,<html>
+ <head>
+ <title>Multi-process tab</title>
+ </head>
+ <body>
+ <iframe
+ id="inlineFrameExample"
+ title="Inline Frame Example"
+ width="300"
+ height="200"
+ src="https://example.com">
+ </iframe>
+ </body>
+ </html>`;
+ const tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, tabUrl2);
+ const previewContainer = document.getElementById("tab-preview-panel");
+
+ await openPreview(tab1);
+ Assert.stringMatches(
+ previewContainer.querySelector(".tab-preview-pid").innerText,
+ /^pid: \d+$/,
+ "Tab PID is shown on single process tab"
+ );
+ Assert.equal(
+ previewContainer.querySelector(".tab-preview-activeness").innerText,
+ "",
+ "Tab activeness is not shown on inactive tab"
+ );
+ await closePreviews();
+
+ await openPreview(tab2);
+ Assert.stringMatches(
+ previewContainer.querySelector(".tab-preview-pid").innerText,
+ /^pids: \d+, \d+$/,
+ "Tab PIDs are shown on multi-process tab"
+ );
+ Assert.equal(
+ previewContainer.querySelector(".tab-preview-activeness").innerText,
+ "[A]",
+ "Tab activeness is shown on active tab"
+ );
+ await closePreviews();
+
+ BrowserTestUtils.removeTab(tab1);
+ BrowserTestUtils.removeTab(tab2);
+ await SpecialPowers.popPrefEnv();
+
+ // Move the mouse outside of the tab strip.
+ EventUtils.synthesizeMouseAtCenter(document.documentElement, {
+ type: "mouseover",
+ });
+});
+
+/**
* Verify that non-selected tabs display a thumbnail in their preview
* when browser.tabs.cardPreview.showThumbnails is set to true,
* while the currently selected tab never displays a thumbnail in its preview.
diff --git a/browser/base/content/test/tabs/browser_visibleTabs_contextMenu.js b/browser/base/content/test/tabs/browser_visibleTabs_contextMenu.js
index 202c43ce47..06fdd27d9c 100644
--- a/browser/base/content/test/tabs/browser_visibleTabs_contextMenu.js
+++ b/browser/base/content/test/tabs/browser_visibleTabs_contextMenu.js
@@ -2,11 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-const remoteClientsFixture = [
- { id: 1, name: "Foo" },
- { id: 2, name: "Bar" },
-];
-
add_task(async function test() {
// There should be one tab when we start the test
let [origTab] = gBrowser.visibleTabs;
@@ -16,9 +11,8 @@ add_task(async function test() {
// Check the context menu with two tabs
updateTabContextMenu(origTab);
- is(
- document.getElementById("context_closeTab").disabled,
- false,
+ ok(
+ !document.getElementById("context_closeTab").disabled,
"Close Tab is enabled"
);
@@ -29,11 +23,14 @@ add_task(async function test() {
// Check the context menu with one tab.
updateTabContextMenu(testTab);
- is(
- document.getElementById("context_closeTab").disabled,
- false,
+ ok(
+ !document.getElementById("context_closeTab").disabled,
"Close Tab is enabled when more than one tab exists"
);
+ ok(
+ !document.getElementById("context_closeDuplicateTabs").disabled,
+ "Close duplicate tabs is enabled when more than one tab with the same URL exists"
+ );
// Add a tab that will get pinned
// So now there's one pinned tab, one visible unpinned tab, and one hidden tab
diff --git a/browser/base/content/test/webextensions/browser_aboutaddons_blanktab.js b/browser/base/content/test/webextensions/browser_aboutaddons_blanktab.js
index 228fe71815..1e0814ea96 100644
--- a/browser/base/content/test/webextensions/browser_aboutaddons_blanktab.js
+++ b/browser/base/content/test/webextensions/browser_aboutaddons_blanktab.js
@@ -8,7 +8,7 @@ add_task(async function testBlankTabReusedAboutAddons() {
is(browser, gBrowser.selectedBrowser, "New tab is selected");
// Opening about:addons shouldn't change the selected tab.
- BrowserOpenAddonsMgr();
+ BrowserAddonUI.openAddonsMgr();
is(browser, gBrowser.selectedBrowser, "No new tab was opened");
diff --git a/browser/base/content/test/webextensions/browser_extension_sideloading.js b/browser/base/content/test/webextensions/browser_extension_sideloading.js
index 4e1fe07194..5d8f82a178 100644
--- a/browser/base/content/test/webextensions/browser_extension_sideloading.js
+++ b/browser/base/content/test/webextensions/browser_extension_sideloading.js
@@ -16,11 +16,14 @@ const kSideloaded = true;
async function createWebExtension(details) {
let options = {
manifest: {
+ manifest_version: details.manifest_version ?? 2,
+
browser_specific_settings: { gecko: { id: details.id } },
name: details.name,
permissions: details.permissions,
+ host_permissions: details.host_permissions,
},
};
@@ -86,9 +89,10 @@ add_task(async function test_sideloading() {
const ID2 = "addon2@tests.mozilla.org";
await createWebExtension({
+ manifest_version: 3,
id: ID2,
name: "Test 2",
- permissions: ["<all_urls>"],
+ host_permissions: ["<all_urls>"],
});
const ID3 = "addon3@tests.mozilla.org";
@@ -224,7 +228,7 @@ add_task(async function test_sideloading() {
// Close the hamburger menu and go directly to the addons manager
await gCUITestUtils.hideMainMenu();
- win = await BrowserOpenAddonsMgr(VIEW);
+ win = await BrowserAddonUI.openAddonsMgr(VIEW);
await waitAboutAddonsViewLoaded(win.document);
// about:addons addon entry element.
@@ -293,7 +297,7 @@ add_task(async function test_sideloading() {
// Close the hamburger menu and go to the detail page for this addon
await gCUITestUtils.hideMainMenu();
- win = await BrowserOpenAddonsMgr(
+ win = await BrowserAddonUI.openAddonsMgr(
`addons://detail/${encodeURIComponent(ID3)}`
);
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 22dff8cb38..731e8adea7 100644
--- a/browser/base/content/test/webextensions/browser_permissions_local_file.js
+++ b/browser/base/content/test/webextensions/browser_permissions_local_file.js
@@ -14,7 +14,9 @@ async function installFile(filename) {
MockFilePicker.setFiles([file]);
MockFilePicker.afterOpenCallback = MockFilePicker.cleanup;
- let { document } = await BrowserOpenAddonsMgr("addons://list/extension");
+ let { document } = await BrowserAddonUI.openAddonsMgr(
+ "addons://list/extension"
+ );
// Do the install...
await waitAboutAddonsViewLoaded(document);
diff --git a/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js b/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js
index 0b0b912503..9ad3deaae1 100644
--- a/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js
+++ b/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js
@@ -36,7 +36,7 @@ async function testUpdateNoPrompt(
is(addon.version, initialVersion, "Version 1 of the addon is installed");
// Go to Extensions in about:addons
- let win = await BrowserOpenAddonsMgr("addons://list/extension");
+ let win = await BrowserAddonUI.openAddonsMgr("addons://list/extension");
await waitAboutAddonsViewLoaded(win.document);
diff --git a/browser/base/content/test/webextensions/head.js b/browser/base/content/test/webextensions/head.js
index f1183c61b8..f364dbed88 100644
--- a/browser/base/content/test/webextensions/head.js
+++ b/browser/base/content/test/webextensions/head.js
@@ -508,7 +508,7 @@ async function interactiveUpdateTest(autoUpdate, checkFn) {
ok(addon, "Addon was installed");
is(addon.version, "1.0", "Version 1 of the addon is installed");
- let win = await BrowserOpenAddonsMgr("addons://list/extension");
+ let win = await BrowserAddonUI.openAddonsMgr("addons://list/extension");
await waitAboutAddonsViewLoaded(win.document);
diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js
index dc3aeffaed..1d8637db2e 100644
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -113,11 +113,6 @@ function getRootEvent(aEvent) {
return BrowserUtils.getRootEvent(aEvent);
}
-// This is here for historical reasons. bug 1742889 covers cleaning this up.
-function whereToOpenLink(e, ignoreButton, ignoreAlt) {
- return BrowserUtils.whereToOpenLink(e, ignoreButton, ignoreAlt);
-}
-
function openTrustedLinkIn(url, where, params) {
URILoadingHelper.openTrustedLinkIn(window, url, where, params);
}