From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mail/base/content/FilterListDialog.js | 1162 ++++ comm/mail/base/content/FilterListDialog.xhtml | 168 + comm/mail/base/content/SearchDialog.js | 650 ++ comm/mail/base/content/SearchDialog.xhtml | 151 + comm/mail/base/content/about3Pane.js | 7260 ++++++++++++++++++++ comm/mail/base/content/about3Pane.xhtml | 762 ++ comm/mail/base/content/aboutAddonsExtra.js | 211 + comm/mail/base/content/aboutDialog-appUpdater.js | 320 + comm/mail/base/content/aboutDialog.css | 187 + comm/mail/base/content/aboutDialog.js | 155 + comm/mail/base/content/aboutDialog.xhtml | 184 + comm/mail/base/content/aboutMessage.js | 617 ++ comm/mail/base/content/aboutMessage.xhtml | 158 + comm/mail/base/content/aboutRights.xhtml | 110 + comm/mail/base/content/browserRequest.js | 145 + comm/mail/base/content/browserRequest.xhtml | 58 + comm/mail/base/content/buildconfig.html | 106 + comm/mail/base/content/commonDialog.xhtml | 110 + comm/mail/base/content/compactFoldersDialog.js | 54 + comm/mail/base/content/compactFoldersDialog.xhtml | 54 + comm/mail/base/content/contentAreaClick.js | 206 + comm/mail/base/content/customElements.js | 35 + comm/mail/base/content/customizeToolbar.js | 836 +++ comm/mail/base/content/customizeToolbar.xhtml | 105 + comm/mail/base/content/dialogShadowDom.js | 14 + comm/mail/base/content/editContactPanel.inc.xhtml | 75 + comm/mail/base/content/editContactPanel.js | 248 + comm/mail/base/content/folderDisplay.js | 2649 +++++++ comm/mail/base/content/globalOverlay.js | 122 + comm/mail/base/content/glodaFacetTab.js | 111 + comm/mail/base/content/glodaFacetView.js | 1114 +++ comm/mail/base/content/glodaFacetView.xhtml | 123 + comm/mail/base/content/glodaFacetViewWrapper.xhtml | 54 + comm/mail/base/content/glodaFacetVis.js | 428 ++ comm/mail/base/content/helpMenu.inc.xhtml | 43 + comm/mail/base/content/hiddenWindowMac.js | 124 + comm/mail/base/content/hiddenWindowMac.xhtml | 101 + comm/mail/base/content/macMessengerMenu.js | 99 + comm/mail/base/content/macWindowMenu.inc.xhtml | 22 + comm/mail/base/content/mail-offline.js | 276 + comm/mail/base/content/mail3PaneWindowCommands.js | 456 ++ comm/mail/base/content/mailCommands.js | 667 ++ comm/mail/base/content/mailCommon.js | 1126 +++ comm/mail/base/content/mailContext.inc.xhtml | 324 + comm/mail/base/content/mailContext.js | 822 +++ comm/mail/base/content/mailCore.js | 1063 +++ comm/mail/base/content/mailTabs.js | 390 ++ comm/mail/base/content/mailWindow.js | 1153 ++++ comm/mail/base/content/mailWindowOverlay.js | 2177 ++++++ comm/mail/base/content/mainCommandSet.inc.xhtml | 247 + comm/mail/base/content/mainKeySet.inc.xhtml | 264 + comm/mail/base/content/mainStatusbar.inc.xhtml | 19 + comm/mail/base/content/messageWindow.js | 742 ++ comm/mail/base/content/messageWindow.xhtml | 484 ++ comm/mail/base/content/messenger-customization.js | 185 + comm/mail/base/content/messenger-doctype.inc.dtd | 42 + comm/mail/base/content/messenger-menubar.inc.xhtml | 1271 ++++ .../content/messenger-titlebar-items.inc.xhtml | 24 + comm/mail/base/content/messenger.js | 1289 ++++ comm/mail/base/content/messenger.xhtml | 671 ++ comm/mail/base/content/migrationProgress.js | 64 + comm/mail/base/content/migrationProgress.xhtml | 39 + comm/mail/base/content/minimizeToTray.js | 19 + .../base/content/modules/thread-pane-columns.mjs | 385 ++ comm/mail/base/content/msgAttachmentView.inc.xhtml | 102 + comm/mail/base/content/msgHdrPopup.inc.xhtml | 224 + comm/mail/base/content/msgHdrView.inc.xhtml | 559 ++ comm/mail/base/content/msgHdrView.js | 4501 ++++++++++++ comm/mail/base/content/msgSecurityPane.inc.xhtml | 131 + comm/mail/base/content/msgSecurityPane.js | 111 + comm/mail/base/content/msgViewNavigation.js | 207 + comm/mail/base/content/multimessageview.js | 844 +++ comm/mail/base/content/multimessageview.xhtml | 79 + comm/mail/base/content/newTagDialog.js | 108 + comm/mail/base/content/newTagDialog.xhtml | 30 + .../base/content/overrides/app-license-body.html | 1274 ++++ .../base/content/overrides/app-license-list.html | 33 + .../base/content/overrides/app-license-name.html | 1 + comm/mail/base/content/overrides/app-license.html | 9 + comm/mail/base/content/printUtils.js | 428 ++ comm/mail/base/content/profileDowngrade.js | 53 + comm/mail/base/content/profileDowngrade.xhtml | 48 + comm/mail/base/content/protovis-r2.6-modded.js | 5349 ++++++++++++++ comm/mail/base/content/quickFilterBar.inc.xhtml | 118 + comm/mail/base/content/quickFilterBar.js | 603 ++ comm/mail/base/content/sanitize.js | 241 + comm/mail/base/content/sanitize.xhtml | 92 + comm/mail/base/content/sanitizeDialog.js | 206 + comm/mail/base/content/searchBar.js | 45 + comm/mail/base/content/selectionsummaries.js | 103 + comm/mail/base/content/shortcutsOverlay.js | 123 + comm/mail/base/content/spacesToolbar.inc.xhtml | 166 + comm/mail/base/content/spacesToolbar.js | 1325 ++++ comm/mail/base/content/spacesToolbarPin.inc.xhtml | 46 + comm/mail/base/content/specialTabs.js | 1320 ++++ comm/mail/base/content/sync.js | 157 + comm/mail/base/content/systemIntegrationDialog.js | 188 + .../base/content/systemIntegrationDialog.xhtml | 53 + comm/mail/base/content/tabDialogs.inc.xhtml | 23 + comm/mail/base/content/tabmail.js | 2048 ++++++ comm/mail/base/content/tagDialog.inc.xhtml | 27 + comm/mail/base/content/threadPane.js | 825 +++ comm/mail/base/content/threadTree.inc.xhtml | 230 + comm/mail/base/content/toolbarIconColor.js | 166 + comm/mail/base/content/troubleshootMode.js | 74 + comm/mail/base/content/troubleshootMode.xhtml | 54 + comm/mail/base/content/utilityOverlay.js | 514 ++ comm/mail/base/content/viewSource.js | 168 + comm/mail/base/content/viewSource.xhtml | 245 + comm/mail/base/content/viewZoomOverlay.js | 153 + comm/mail/base/content/webextensions.css | 106 + .../base/content/widgets/browserPopups.inc.xhtml | 192 + comm/mail/base/content/widgets/browserPopups.js | 991 +++ .../base/content/widgets/customizable-toolbar.js | 319 + comm/mail/base/content/widgets/foldersummary.js | 295 + .../content/widgets/gloda-autocomplete-input.js | 243 + comm/mail/base/content/widgets/glodaFacet.js | 1823 +++++ comm/mail/base/content/widgets/header-fields.js | 973 +++ comm/mail/base/content/widgets/mailWidgets.js | 2477 +++++++ comm/mail/base/content/widgets/pane-splitter.js | 562 ++ comm/mail/base/content/widgets/statuspanel.js | 78 + comm/mail/base/content/widgets/tabmail-tab.js | 179 + comm/mail/base/content/widgets/tabmail-tabs.js | 723 ++ .../base/content/widgets/toolbarContext.inc.xhtml | 19 + .../content/widgets/toolbarbutton-menu-button.js | 80 + comm/mail/base/content/widgets/tree-listbox.js | 914 +++ comm/mail/base/content/widgets/tree-selection.mjs | 744 ++ comm/mail/base/content/widgets/tree-view.mjs | 2633 +++++++ comm/mail/base/jar.mn | 144 + comm/mail/base/moz.build | 54 + .../base/test/browser/browser-detachedWindows.ini | 15 + .../test/browser/browser-drawBelowTitlebar.ini | 17 + .../base/test/browser/browser-drawInTitlebar.ini | 17 + comm/mail/base/test/browser/browser.ini | 66 + .../base/test/browser/browser_3paneTelemetry.js | 163 + comm/mail/base/test/browser/browser_archive.js | 98 + .../base/test/browser/browser_browserContext.js | 398 ++ .../test/browser/browser_browserRequestWindow.js | 74 + comm/mail/base/test/browser/browser_cardsView.js | 248 + .../base/test/browser/browser_detachedWindows.js | 223 + comm/mail/base/test/browser/browser_editMenu.js | 511 ++ comm/mail/base/test/browser/browser_fileMenu.js | 137 + .../base/test/browser/browser_folderPaneContext.js | 198 + .../test/browser/browser_folderTreeProperties.js | 236 + .../base/test/browser/browser_folderTreeQuirks.js | 1450 ++++ comm/mail/base/test/browser/browser_formPickers.js | 352 + comm/mail/base/test/browser/browser_goMenu.js | 35 + .../test/browser/browser_interactionTelemetry.js | 67 + comm/mail/base/test/browser/browser_linkHandler.js | 294 + comm/mail/base/test/browser/browser_mailContext.js | 950 +++ .../test/browser/browser_mailTabsAndWindows.js | 355 + comm/mail/base/test/browser/browser_markAsRead.js | 204 + comm/mail/base/test/browser/browser_menulist.js | 183 + comm/mail/base/test/browser/browser_messageMenu.js | 355 + comm/mail/base/test/browser/browser_navigation.js | 1035 +++ .../test/browser/browser_orderableTreeListbox.js | 481 ++ comm/mail/base/test/browser/browser_paneFocus.js | 375 + .../mail/base/test/browser/browser_paneSplitter.js | 572 ++ .../base/test/browser/browser_preferDisplayName.js | 456 ++ .../base/test/browser/browser_searchMessages.js | 460 ++ .../browser/browser_selectionWidgetController.js | 6196 +++++++++++++++++ .../base/test/browser/browser_smartFolderDelete.js | 75 + .../base/test/browser/browser_spacesToolbar.js | 1173 ++++ .../test/browser/browser_spacesToolbarCustomize.js | 119 + .../browser_spacesToolbar_drawBelowTitlebar.js | 24 + .../browser_spacesToolbar_drawInTitlebar.js | 24 + .../base/test/browser/browser_statusFeedback.js | 71 + comm/mail/base/test/browser/browser_tabIcon.js | 99 + comm/mail/base/test/browser/browser_tagsMode.js | 214 + .../test/browser/browser_threadTreeDeleting.js | 572 ++ .../base/test/browser/browser_threadTreeQuirks.js | 669 ++ .../base/test/browser/browser_threadTreeSorting.js | 344 + comm/mail/base/test/browser/browser_threads.js | 385 ++ comm/mail/base/test/browser/browser_toolsMenu.js | 109 + comm/mail/base/test/browser/browser_treeListbox.js | 1313 ++++ comm/mail/base/test/browser/browser_treeView.js | 1941 ++++++ comm/mail/base/test/browser/browser_viewMenu.js | 218 + .../test/browser/browser_webSearchTelemetry.js | 50 + comm/mail/base/test/browser/browser_zoom.js | 110 + comm/mail/base/test/browser/files/formContent.html | 36 + comm/mail/base/test/browser/files/links.html | 38 + comm/mail/base/test/browser/files/menulist.xhtml | 30 + .../test/browser/files/orderableTreeListbox.xhtml | 171 + .../base/test/browser/files/paneSplitter.xhtml | 122 + comm/mail/base/test/browser/files/rss.xml | 16 + .../mail/base/test/browser/files/sampleContent.eml | 160 + .../base/test/browser/files/sampleContent.html | 16 + .../base/test/browser/files/selectionWidget.js | 225 + .../base/test/browser/files/selectionWidget.xhtml | 57 + comm/mail/base/test/browser/files/tb-logo.png | Bin 0 -> 6462 bytes .../test/browser/files/tree-element-test-common.js | 73 + .../test/browser/files/tree-element-test-header.js | 64 + .../browser/files/tree-element-test-header.xhtml | 61 + .../test/browser/files/tree-element-test-levels.js | 118 + .../browser/files/tree-element-test-levels.xhtml | 65 + .../browser/files/tree-element-test-no-header.js | 58 + .../files/tree-element-test-no-header.xhtml | 54 + .../mail/base/test/browser/files/treeListbox.xhtml | 390 ++ comm/mail/base/test/browser/head.js | 371 + comm/mail/base/test/browser/head_spacesToolbar.js | 34 + comm/mail/base/test/moz.build | 22 + comm/mail/base/test/performance/browser.ini | 23 + .../test/performance/browser_preferences_usage.js | 177 + comm/mail/base/test/performance/browser_startup.js | 277 + comm/mail/base/test/unit/distribution.ini | 56 + comm/mail/base/test/unit/head_mailbase.js | 20 + comm/mail/base/test/unit/head_mailbase_maildir.js | 9 + .../test/unit/resources/viewWrapperTestUtils.js | 534 ++ comm/mail/base/test/unit/test_alertHook.js | 119 + comm/mail/base/test/unit/test_attachmentChecker.js | 121 + comm/mail/base/test/unit/test_devtools_url.js | 22 + .../test/unit/test_emptyTrash_dbViewWrapper.js | 43 + .../base/test/unit/test_mailGlue_distribution.js | 120 + comm/mail/base/test/unit/test_oauth_migration.js | 319 + comm/mail/base/test/unit/test_treeSelection.js | 581 ++ .../base/test/unit/test_viewWrapper_imapFolder.js | 55 + comm/mail/base/test/unit/test_viewWrapper_logic.js | 359 + .../base/test/unit/test_viewWrapper_realFolder.js | 666 ++ .../test/unit/test_viewWrapper_virtualFolder.js | 552 ++ .../test_viewWrapper_virtualFolderCustomTerm.js | 65 + comm/mail/base/test/unit/xpcshell.ini | 25 + comm/mail/base/test/unit/xpcshell_maildir.ini | 6 + comm/mail/base/test/webextensions/.eslintrc.js | 13 + comm/mail/base/test/webextensions/browser.ini | 41 + .../browser_extension_install_experiment.js | 82 + .../webextensions/browser_extension_sideloading.js | 352 + .../browser_extension_update_background.js | 263 + ...browser_extension_update_background_noprompt.js | 116 + .../browser_permissions_installTrigger.js | 27 + .../browser_permissions_local_file.js | 46 + .../browser_permissions_mozAddonManager.js | 19 + .../webextensions/browser_permissions_optional.js | 53 + .../browser_permissions_pointerevent.js | 65 + .../webextensions/browser_permissions_unsigned.js | 49 + .../browser_update_checkForUpdates.js | 17 + .../browser_update_interactive_noprompt.js | 82 + .../webextensions/browser_webext_experiment.xpi | Bin 0 -> 2492 bytes .../browser_webext_experiment_permissions.xpi | Bin 0 -> 2510 bytes .../browser_webext_experiment_update1.xpi | Bin 0 -> 331 bytes .../browser_webext_experiment_update2.xpi | Bin 0 -> 2547 bytes .../webextensions/browser_webext_nopermissions.xpi | Bin 0 -> 4273 bytes .../webextensions/browser_webext_permissions.xpi | Bin 0 -> 16638 bytes .../test/webextensions/browser_webext_unsigned.xpi | Bin 0 -> 12606 bytes .../test/webextensions/browser_webext_update.json | 82 + .../test/webextensions/browser_webext_update1.xpi | Bin 0 -> 4311 bytes .../test/webextensions/browser_webext_update2.xpi | Bin 0 -> 4331 bytes .../webextensions/browser_webext_update_icon1.xpi | Bin 0 -> 16585 bytes .../webextensions/browser_webext_update_icon2.xpi | Bin 0 -> 16604 bytes .../browser_webext_update_origins1.xpi | Bin 0 -> 268 bytes .../browser_webext_update_origins2.xpi | Bin 0 -> 275 bytes .../webextensions/browser_webext_update_perms1.xpi | Bin 0 -> 4273 bytes .../webextensions/browser_webext_update_perms2.xpi | Bin 0 -> 4282 bytes .../webextensions/file_install_extensions.html | 19 + comm/mail/base/test/webextensions/head.js | 632 ++ 254 files changed, 102302 insertions(+) create mode 100644 comm/mail/base/content/FilterListDialog.js create mode 100644 comm/mail/base/content/FilterListDialog.xhtml create mode 100644 comm/mail/base/content/SearchDialog.js create mode 100644 comm/mail/base/content/SearchDialog.xhtml create mode 100644 comm/mail/base/content/about3Pane.js create mode 100644 comm/mail/base/content/about3Pane.xhtml create mode 100644 comm/mail/base/content/aboutAddonsExtra.js create mode 100644 comm/mail/base/content/aboutDialog-appUpdater.js create mode 100644 comm/mail/base/content/aboutDialog.css create mode 100644 comm/mail/base/content/aboutDialog.js create mode 100644 comm/mail/base/content/aboutDialog.xhtml create mode 100644 comm/mail/base/content/aboutMessage.js create mode 100644 comm/mail/base/content/aboutMessage.xhtml create mode 100644 comm/mail/base/content/aboutRights.xhtml create mode 100644 comm/mail/base/content/browserRequest.js create mode 100644 comm/mail/base/content/browserRequest.xhtml create mode 100644 comm/mail/base/content/buildconfig.html create mode 100644 comm/mail/base/content/commonDialog.xhtml create mode 100644 comm/mail/base/content/compactFoldersDialog.js create mode 100644 comm/mail/base/content/compactFoldersDialog.xhtml create mode 100644 comm/mail/base/content/contentAreaClick.js create mode 100644 comm/mail/base/content/customElements.js create mode 100644 comm/mail/base/content/customizeToolbar.js create mode 100644 comm/mail/base/content/customizeToolbar.xhtml create mode 100644 comm/mail/base/content/dialogShadowDom.js create mode 100644 comm/mail/base/content/editContactPanel.inc.xhtml create mode 100644 comm/mail/base/content/editContactPanel.js create mode 100644 comm/mail/base/content/folderDisplay.js create mode 100644 comm/mail/base/content/globalOverlay.js create mode 100644 comm/mail/base/content/glodaFacetTab.js create mode 100644 comm/mail/base/content/glodaFacetView.js create mode 100644 comm/mail/base/content/glodaFacetView.xhtml create mode 100644 comm/mail/base/content/glodaFacetViewWrapper.xhtml create mode 100644 comm/mail/base/content/glodaFacetVis.js create mode 100644 comm/mail/base/content/helpMenu.inc.xhtml create mode 100644 comm/mail/base/content/hiddenWindowMac.js create mode 100644 comm/mail/base/content/hiddenWindowMac.xhtml create mode 100644 comm/mail/base/content/macMessengerMenu.js create mode 100644 comm/mail/base/content/macWindowMenu.inc.xhtml create mode 100644 comm/mail/base/content/mail-offline.js create mode 100644 comm/mail/base/content/mail3PaneWindowCommands.js create mode 100644 comm/mail/base/content/mailCommands.js create mode 100644 comm/mail/base/content/mailCommon.js create mode 100644 comm/mail/base/content/mailContext.inc.xhtml create mode 100644 comm/mail/base/content/mailContext.js create mode 100644 comm/mail/base/content/mailCore.js create mode 100644 comm/mail/base/content/mailTabs.js create mode 100644 comm/mail/base/content/mailWindow.js create mode 100644 comm/mail/base/content/mailWindowOverlay.js create mode 100644 comm/mail/base/content/mainCommandSet.inc.xhtml create mode 100644 comm/mail/base/content/mainKeySet.inc.xhtml create mode 100644 comm/mail/base/content/mainStatusbar.inc.xhtml create mode 100644 comm/mail/base/content/messageWindow.js create mode 100644 comm/mail/base/content/messageWindow.xhtml create mode 100644 comm/mail/base/content/messenger-customization.js create mode 100644 comm/mail/base/content/messenger-doctype.inc.dtd create mode 100644 comm/mail/base/content/messenger-menubar.inc.xhtml create mode 100644 comm/mail/base/content/messenger-titlebar-items.inc.xhtml create mode 100644 comm/mail/base/content/messenger.js create mode 100644 comm/mail/base/content/messenger.xhtml create mode 100644 comm/mail/base/content/migrationProgress.js create mode 100644 comm/mail/base/content/migrationProgress.xhtml create mode 100644 comm/mail/base/content/minimizeToTray.js create mode 100644 comm/mail/base/content/modules/thread-pane-columns.mjs create mode 100644 comm/mail/base/content/msgAttachmentView.inc.xhtml create mode 100644 comm/mail/base/content/msgHdrPopup.inc.xhtml create mode 100644 comm/mail/base/content/msgHdrView.inc.xhtml create mode 100644 comm/mail/base/content/msgHdrView.js create mode 100644 comm/mail/base/content/msgSecurityPane.inc.xhtml create mode 100644 comm/mail/base/content/msgSecurityPane.js create mode 100644 comm/mail/base/content/msgViewNavigation.js create mode 100644 comm/mail/base/content/multimessageview.js create mode 100644 comm/mail/base/content/multimessageview.xhtml create mode 100644 comm/mail/base/content/newTagDialog.js create mode 100644 comm/mail/base/content/newTagDialog.xhtml create mode 100644 comm/mail/base/content/overrides/app-license-body.html create mode 100644 comm/mail/base/content/overrides/app-license-list.html create mode 100644 comm/mail/base/content/overrides/app-license-name.html create mode 100644 comm/mail/base/content/overrides/app-license.html create mode 100644 comm/mail/base/content/printUtils.js create mode 100644 comm/mail/base/content/profileDowngrade.js create mode 100644 comm/mail/base/content/profileDowngrade.xhtml create mode 100644 comm/mail/base/content/protovis-r2.6-modded.js create mode 100644 comm/mail/base/content/quickFilterBar.inc.xhtml create mode 100644 comm/mail/base/content/quickFilterBar.js create mode 100644 comm/mail/base/content/sanitize.js create mode 100644 comm/mail/base/content/sanitize.xhtml create mode 100644 comm/mail/base/content/sanitizeDialog.js create mode 100644 comm/mail/base/content/searchBar.js create mode 100644 comm/mail/base/content/selectionsummaries.js create mode 100644 comm/mail/base/content/shortcutsOverlay.js create mode 100644 comm/mail/base/content/spacesToolbar.inc.xhtml create mode 100644 comm/mail/base/content/spacesToolbar.js create mode 100644 comm/mail/base/content/spacesToolbarPin.inc.xhtml create mode 100644 comm/mail/base/content/specialTabs.js create mode 100644 comm/mail/base/content/sync.js create mode 100644 comm/mail/base/content/systemIntegrationDialog.js create mode 100644 comm/mail/base/content/systemIntegrationDialog.xhtml create mode 100644 comm/mail/base/content/tabDialogs.inc.xhtml create mode 100644 comm/mail/base/content/tabmail.js create mode 100644 comm/mail/base/content/tagDialog.inc.xhtml create mode 100644 comm/mail/base/content/threadPane.js create mode 100644 comm/mail/base/content/threadTree.inc.xhtml create mode 100644 comm/mail/base/content/toolbarIconColor.js create mode 100644 comm/mail/base/content/troubleshootMode.js create mode 100644 comm/mail/base/content/troubleshootMode.xhtml create mode 100644 comm/mail/base/content/utilityOverlay.js create mode 100644 comm/mail/base/content/viewSource.js create mode 100644 comm/mail/base/content/viewSource.xhtml create mode 100644 comm/mail/base/content/viewZoomOverlay.js create mode 100644 comm/mail/base/content/webextensions.css create mode 100644 comm/mail/base/content/widgets/browserPopups.inc.xhtml create mode 100644 comm/mail/base/content/widgets/browserPopups.js create mode 100644 comm/mail/base/content/widgets/customizable-toolbar.js create mode 100644 comm/mail/base/content/widgets/foldersummary.js create mode 100644 comm/mail/base/content/widgets/gloda-autocomplete-input.js create mode 100644 comm/mail/base/content/widgets/glodaFacet.js create mode 100644 comm/mail/base/content/widgets/header-fields.js create mode 100644 comm/mail/base/content/widgets/mailWidgets.js create mode 100644 comm/mail/base/content/widgets/pane-splitter.js create mode 100644 comm/mail/base/content/widgets/statuspanel.js create mode 100644 comm/mail/base/content/widgets/tabmail-tab.js create mode 100644 comm/mail/base/content/widgets/tabmail-tabs.js create mode 100644 comm/mail/base/content/widgets/toolbarContext.inc.xhtml create mode 100644 comm/mail/base/content/widgets/toolbarbutton-menu-button.js create mode 100644 comm/mail/base/content/widgets/tree-listbox.js create mode 100644 comm/mail/base/content/widgets/tree-selection.mjs create mode 100644 comm/mail/base/content/widgets/tree-view.mjs create mode 100644 comm/mail/base/jar.mn create mode 100644 comm/mail/base/moz.build create mode 100644 comm/mail/base/test/browser/browser-detachedWindows.ini create mode 100644 comm/mail/base/test/browser/browser-drawBelowTitlebar.ini create mode 100644 comm/mail/base/test/browser/browser-drawInTitlebar.ini create mode 100644 comm/mail/base/test/browser/browser.ini create mode 100644 comm/mail/base/test/browser/browser_3paneTelemetry.js create mode 100644 comm/mail/base/test/browser/browser_archive.js create mode 100644 comm/mail/base/test/browser/browser_browserContext.js create mode 100644 comm/mail/base/test/browser/browser_browserRequestWindow.js create mode 100644 comm/mail/base/test/browser/browser_cardsView.js create mode 100644 comm/mail/base/test/browser/browser_detachedWindows.js create mode 100644 comm/mail/base/test/browser/browser_editMenu.js create mode 100644 comm/mail/base/test/browser/browser_fileMenu.js create mode 100644 comm/mail/base/test/browser/browser_folderPaneContext.js create mode 100644 comm/mail/base/test/browser/browser_folderTreeProperties.js create mode 100644 comm/mail/base/test/browser/browser_folderTreeQuirks.js create mode 100644 comm/mail/base/test/browser/browser_formPickers.js create mode 100644 comm/mail/base/test/browser/browser_goMenu.js create mode 100644 comm/mail/base/test/browser/browser_interactionTelemetry.js create mode 100644 comm/mail/base/test/browser/browser_linkHandler.js create mode 100644 comm/mail/base/test/browser/browser_mailContext.js create mode 100644 comm/mail/base/test/browser/browser_mailTabsAndWindows.js create mode 100644 comm/mail/base/test/browser/browser_markAsRead.js create mode 100644 comm/mail/base/test/browser/browser_menulist.js create mode 100644 comm/mail/base/test/browser/browser_messageMenu.js create mode 100644 comm/mail/base/test/browser/browser_navigation.js create mode 100644 comm/mail/base/test/browser/browser_orderableTreeListbox.js create mode 100644 comm/mail/base/test/browser/browser_paneFocus.js create mode 100644 comm/mail/base/test/browser/browser_paneSplitter.js create mode 100644 comm/mail/base/test/browser/browser_preferDisplayName.js create mode 100644 comm/mail/base/test/browser/browser_searchMessages.js create mode 100644 comm/mail/base/test/browser/browser_selectionWidgetController.js create mode 100644 comm/mail/base/test/browser/browser_smartFolderDelete.js create mode 100644 comm/mail/base/test/browser/browser_spacesToolbar.js create mode 100644 comm/mail/base/test/browser/browser_spacesToolbarCustomize.js create mode 100644 comm/mail/base/test/browser/browser_spacesToolbar_drawBelowTitlebar.js create mode 100644 comm/mail/base/test/browser/browser_spacesToolbar_drawInTitlebar.js create mode 100644 comm/mail/base/test/browser/browser_statusFeedback.js create mode 100644 comm/mail/base/test/browser/browser_tabIcon.js create mode 100644 comm/mail/base/test/browser/browser_tagsMode.js create mode 100644 comm/mail/base/test/browser/browser_threadTreeDeleting.js create mode 100644 comm/mail/base/test/browser/browser_threadTreeQuirks.js create mode 100644 comm/mail/base/test/browser/browser_threadTreeSorting.js create mode 100644 comm/mail/base/test/browser/browser_threads.js create mode 100644 comm/mail/base/test/browser/browser_toolsMenu.js create mode 100644 comm/mail/base/test/browser/browser_treeListbox.js create mode 100644 comm/mail/base/test/browser/browser_treeView.js create mode 100644 comm/mail/base/test/browser/browser_viewMenu.js create mode 100644 comm/mail/base/test/browser/browser_webSearchTelemetry.js create mode 100644 comm/mail/base/test/browser/browser_zoom.js create mode 100644 comm/mail/base/test/browser/files/formContent.html create mode 100644 comm/mail/base/test/browser/files/links.html create mode 100644 comm/mail/base/test/browser/files/menulist.xhtml create mode 100644 comm/mail/base/test/browser/files/orderableTreeListbox.xhtml create mode 100644 comm/mail/base/test/browser/files/paneSplitter.xhtml create mode 100644 comm/mail/base/test/browser/files/rss.xml create mode 100644 comm/mail/base/test/browser/files/sampleContent.eml create mode 100644 comm/mail/base/test/browser/files/sampleContent.html create mode 100644 comm/mail/base/test/browser/files/selectionWidget.js create mode 100644 comm/mail/base/test/browser/files/selectionWidget.xhtml create mode 100644 comm/mail/base/test/browser/files/tb-logo.png create mode 100644 comm/mail/base/test/browser/files/tree-element-test-common.js create mode 100644 comm/mail/base/test/browser/files/tree-element-test-header.js create mode 100644 comm/mail/base/test/browser/files/tree-element-test-header.xhtml create mode 100644 comm/mail/base/test/browser/files/tree-element-test-levels.js create mode 100644 comm/mail/base/test/browser/files/tree-element-test-levels.xhtml create mode 100644 comm/mail/base/test/browser/files/tree-element-test-no-header.js create mode 100644 comm/mail/base/test/browser/files/tree-element-test-no-header.xhtml create mode 100644 comm/mail/base/test/browser/files/treeListbox.xhtml create mode 100644 comm/mail/base/test/browser/head.js create mode 100644 comm/mail/base/test/browser/head_spacesToolbar.js create mode 100644 comm/mail/base/test/moz.build create mode 100644 comm/mail/base/test/performance/browser.ini create mode 100644 comm/mail/base/test/performance/browser_preferences_usage.js create mode 100644 comm/mail/base/test/performance/browser_startup.js create mode 100644 comm/mail/base/test/unit/distribution.ini create mode 100644 comm/mail/base/test/unit/head_mailbase.js create mode 100644 comm/mail/base/test/unit/head_mailbase_maildir.js create mode 100644 comm/mail/base/test/unit/resources/viewWrapperTestUtils.js create mode 100644 comm/mail/base/test/unit/test_alertHook.js create mode 100644 comm/mail/base/test/unit/test_attachmentChecker.js create mode 100644 comm/mail/base/test/unit/test_devtools_url.js create mode 100644 comm/mail/base/test/unit/test_emptyTrash_dbViewWrapper.js create mode 100644 comm/mail/base/test/unit/test_mailGlue_distribution.js create mode 100644 comm/mail/base/test/unit/test_oauth_migration.js create mode 100644 comm/mail/base/test/unit/test_treeSelection.js create mode 100644 comm/mail/base/test/unit/test_viewWrapper_imapFolder.js create mode 100644 comm/mail/base/test/unit/test_viewWrapper_logic.js create mode 100644 comm/mail/base/test/unit/test_viewWrapper_realFolder.js create mode 100644 comm/mail/base/test/unit/test_viewWrapper_virtualFolder.js create mode 100644 comm/mail/base/test/unit/test_viewWrapper_virtualFolderCustomTerm.js create mode 100644 comm/mail/base/test/unit/xpcshell.ini create mode 100644 comm/mail/base/test/unit/xpcshell_maildir.ini create mode 100644 comm/mail/base/test/webextensions/.eslintrc.js create mode 100644 comm/mail/base/test/webextensions/browser.ini create mode 100644 comm/mail/base/test/webextensions/browser_extension_install_experiment.js create mode 100644 comm/mail/base/test/webextensions/browser_extension_sideloading.js create mode 100644 comm/mail/base/test/webextensions/browser_extension_update_background.js create mode 100644 comm/mail/base/test/webextensions/browser_extension_update_background_noprompt.js create mode 100644 comm/mail/base/test/webextensions/browser_permissions_installTrigger.js create mode 100644 comm/mail/base/test/webextensions/browser_permissions_local_file.js create mode 100644 comm/mail/base/test/webextensions/browser_permissions_mozAddonManager.js create mode 100644 comm/mail/base/test/webextensions/browser_permissions_optional.js create mode 100644 comm/mail/base/test/webextensions/browser_permissions_pointerevent.js create mode 100644 comm/mail/base/test/webextensions/browser_permissions_unsigned.js create mode 100644 comm/mail/base/test/webextensions/browser_update_checkForUpdates.js create mode 100644 comm/mail/base/test/webextensions/browser_update_interactive_noprompt.js create mode 100644 comm/mail/base/test/webextensions/browser_webext_experiment.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_experiment_permissions.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_experiment_update1.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_experiment_update2.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_nopermissions.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_permissions.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_unsigned.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update.json create mode 100644 comm/mail/base/test/webextensions/browser_webext_update1.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update2.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update_icon1.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update_icon2.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update_origins1.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update_origins2.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update_perms1.xpi create mode 100644 comm/mail/base/test/webextensions/browser_webext_update_perms2.xpi create mode 100644 comm/mail/base/test/webextensions/file_install_extensions.html create mode 100644 comm/mail/base/test/webextensions/head.js (limited to 'comm/mail/base') diff --git a/comm/mail/base/content/FilterListDialog.js b/comm/mail/base/content/FilterListDialog.js new file mode 100644 index 0000000000..a802da6d78 --- /dev/null +++ b/comm/mail/base/content/FilterListDialog.js @@ -0,0 +1,1162 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +var { PluralForm } = ChromeUtils.importESModule( + "resource://gre/modules/PluralForm.sys.mjs" +); +var { MailServices } = ChromeUtils.import( + "resource:///modules/MailServices.jsm" +); + +window.addEventListener("load", onLoad); +window.addEventListener("unload", onFilterUnload); +window.addEventListener("close", event => { + if (!onFilterClose()) { + event.preventDefault(); + } +}); + +var gFilterListMsgWindow = null; +var gCurrentFilterList; +var gServerMenu = null; +var gFilterListbox = null; +var gEditButton = null; +var gDeleteButton = null; +var gCopyToNewButton = null; +var gTopButton = null; +var gUpButton = null; +var gDownButton = null; +var gBottomButton = null; +var gSearchBox = null; +var gRunFiltersFolder = null; +var gRunFiltersButton = null; + +var gFilterBundle = null; + +var msgMoveMotion = { + Up: 0, + Down: 1, + Top: 2, + Bottom: 3, +}; + +var gStatusFeedback = { + progressMeterVisible: false, + + showStatusString(status) { + document.getElementById("statusText").setAttribute("value", status); + }, + startMeteors() { + // change run button to be a stop button + gRunFiltersButton.setAttribute( + "label", + gRunFiltersButton.getAttribute("stoplabel") + ); + gRunFiltersButton.setAttribute( + "accesskey", + gRunFiltersButton.getAttribute("stopaccesskey") + ); + + if (!this.progressMeterVisible) { + document + .getElementById("statusbar-progresspanel") + .removeAttribute("collapsed"); + this.progressMeterVisible = true; + } + + document.getElementById("statusbar-icon").removeAttribute("value"); + }, + stopMeteors() { + try { + // change run button to be a stop button + gRunFiltersButton.setAttribute( + "label", + gRunFiltersButton.getAttribute("runlabel") + ); + gRunFiltersButton.setAttribute( + "accesskey", + gRunFiltersButton.getAttribute("runaccesskey") + ); + + if (this.progressMeterVisible) { + document.getElementById("statusbar-progresspanel").collapsed = true; + this.progressMeterVisible = true; + } + } catch (ex) { + // can get here if closing window when running filters + } + }, + showProgress(percentage) {}, + closeWindow() {}, +}; + +var filterEditorQuitObserver = { + observe(aSubject, aTopic, aData) { + // Check whether or not we want to veto the quit request (unless another + // observer already did. + if ( + aTopic == "quit-application-requested" && + aSubject instanceof Ci.nsISupportsPRBool && + !aSubject.data + ) { + aSubject.data = !onFilterClose(); + } + }, +}; + +function onLoad() { + gFilterListMsgWindow = Cc[ + "@mozilla.org/messenger/msgwindow;1" + ].createInstance(Ci.nsIMsgWindow); + gFilterListMsgWindow.domWindow = window; + gFilterListMsgWindow.rootDocShell.appType = Ci.nsIDocShell.APP_TYPE_MAIL; + gFilterListMsgWindow.statusFeedback = gStatusFeedback; + + gServerMenu = document.getElementById("serverMenu"); + gFilterListbox = document.getElementById("filterList"); + gEditButton = document.getElementById("editButton"); + gDeleteButton = document.getElementById("deleteButton"); + gCopyToNewButton = document.getElementById("copyToNewButton"); + gTopButton = document.getElementById("reorderTopButton"); + gUpButton = document.getElementById("reorderUpButton"); + gDownButton = document.getElementById("reorderDownButton"); + gBottomButton = document.getElementById("reorderBottomButton"); + gSearchBox = document.getElementById("searchBox"); + gRunFiltersFolder = document.getElementById("runFiltersFolder"); + gRunFiltersButton = document.getElementById("runFiltersButton"); + gFilterBundle = document.getElementById("bundle_filter"); + + updateButtons(); + + initNewToolbarButtons(document.querySelector("#newButton toolbarbutton")); + initNewToolbarButtons(document.querySelector("#newButton dropmarker")); + document + .getElementById("filterActionButtons") + .addEventListener("keypress", event => onFilterActionButtonKeyPress(event)); + + processWindowArguments(window.arguments[0]); + + // Don't change width after initial layout, so buttons stay within the dialog. + gRunFiltersFolder.style.maxWidth = + gRunFiltersFolder.getBoundingClientRect().width + "px"; + + Services.obs.addObserver( + filterEditorQuitObserver, + "quit-application-requested" + ); +} +/** + * Set up the toolbarbutton to have an index and an EvenListener for proper + * keyboard navigation. + * + * @param {XULElement} newToolbarbutton - The toolbarbutton that needs to be + * initialized. + */ +function initNewToolbarButtons(newToolbarbutton) { + newToolbarbutton.setAttribute("tabindex", "0"); + newToolbarbutton.setAttribute( + "id", + newToolbarbutton.parentNode.id + newToolbarbutton.tagName + ); +} + +/** + * Processes arguments sent to this dialog when opened or refreshed. + * + * @param aArguments An object having members representing the arguments. + * { arg1: value1, arg2: value2, ... } + */ +function processWindowArguments(aArguments) { + // If a specific folder was requested, try to select it + // if we don't already show its server. + if ( + !gServerMenu._folder || + ("folder" in aArguments && + aArguments.folder != gServerMenu._folder && + aArguments.folder.rootFolder != gServerMenu._folder) + ) { + let wantedFolder; + if ("folder" in aArguments) { + wantedFolder = aArguments.folder; + } + + // Get the folder where filters should be defined, if that server + // can accept filters. + let firstItem = getFilterFolderForSelection(wantedFolder); + + // If the selected server cannot have filters, get the default server + // If the default server cannot have filters, check all accounts + // and get a server that can have filters. + if (!firstItem) { + firstItem = getServerThatCanHaveFilters().rootFolder; + } + + if (firstItem) { + setFilterFolder(firstItem); + } + + if (wantedFolder) { + setRunFolder(wantedFolder); + } + } else { + // If we didn't change folder still redraw the list + // to show potential new filters if we were called for refresh. + rebuildFilterList(); + } + + // If a specific filter was requested, try to select it. + if ("filter" in aArguments) { + selectFilter(aArguments.filter); + } +} + +/** + * This is called from OpenOrFocusWindow() if the dialog is already open. + * New filters could have been created by operations outside the dialog. + * + * @param aArguments An object of arguments having the same format + * as window.arguments[0]. + */ +function refresh(aArguments) { + // As we really don't know what has changed, clear the search box + // undonditionally so that the changed/added filters are surely visible. + resetSearchBox(); + + processWindowArguments(aArguments); +} + +function CanRunFiltersAfterTheFact(aServer) { + // filter after the fact is implement using search + // so if you can't search, you can't filter after the fact + return aServer.canSearchMessages; +} + +/** + * Change the root server for which we are managing filters. + * + * @param msgFolder The nsIMsgFolder server containing filters + * (or a folder for NNTP server). + */ +function setFilterFolder(msgFolder) { + if (!msgFolder || msgFolder == gServerMenu._folder) { + return; + } + + // Save the current filters to disk before switching because + // the dialog may be closed and we'll lose current filters. + if (gCurrentFilterList) { + gCurrentFilterList.saveToDefaultFile(); + } + + // Setting this attribute should go away in bug 473009. + gServerMenu._folder = msgFolder; + // Calling this should go away in bug 802609. + gServerMenu.menupopup.selectFolder(msgFolder); + + // Calling getEditableFilterList will detect any errors in msgFilterRules.dat, + // backup the file, and alert the user. + gCurrentFilterList = msgFolder.getEditableFilterList(gFilterListMsgWindow); + rebuildFilterList(); + + // Select the first item in the list, if there is one. + if (gFilterListbox.itemCount > 0) { + gFilterListbox.selectItem(gFilterListbox.getItemAtIndex(0)); + } + + // This will get the deferred to account root folder, if server is deferred. + // We intentionally do this after setting the current server, as we want + // that to refer to the rootFolder for the actual server, not the + // deferred-to server, as current server is really a proxy for the + // server whose filters we are editing. But below here we are managing + // where the filters will get applied, which is on the deferred-to server. + msgFolder = msgFolder.server.rootMsgFolder; + + // root the folder picker to this server + let runMenu = gRunFiltersFolder.menupopup; + runMenu._teardown(); + runMenu._parentFolder = msgFolder; + runMenu._ensureInitialized(); + + let canFilterAfterTheFact = CanRunFiltersAfterTheFact(msgFolder.server); + gRunFiltersFolder.disabled = !canFilterAfterTheFact; + gRunFiltersButton.disabled = !canFilterAfterTheFact; + document.getElementById("folderPickerPrefix").disabled = + !canFilterAfterTheFact; + + if (canFilterAfterTheFact) { + let wantedFolder = null; + // For a given server folder, get the default run target folder or show + // "Choose Folder". + if (!msgFolder.isServer) { + wantedFolder = msgFolder; + } else { + try { + switch (msgFolder.server.type) { + case "nntp": + // For NNTP select the subscribed newsgroup. + wantedFolder = gServerMenu._folder; + break; + case "rss": + // Show "Choose Folder" for feeds. + wantedFolder = null; + break; + case "imap": + case "pop3": + case "none": + // Find Inbox for IMAP and POP or Local Folders, + // show "Choose Folder" if not found. + wantedFolder = msgFolder.rootFolder.getFolderWithFlags( + Ci.nsMsgFolderFlags.Inbox + ); + break; + default: + // For other account types we don't know what's good to select, + // so show "Choose Folder". + wantedFolder = null; + } + } catch (e) { + console.error( + "Failed to select a suitable folder to run filters on: " + e + ); + wantedFolder = null; + } + } + + // Select a useful first folder for the server. + setRunFolder(wantedFolder); + } +} + +/** + * Select a folder on which filters are to be run. + * + * @param aFolder nsIMsgFolder folder to select. + */ +function setRunFolder(aFolder) { + // Setting this attribute should go away in bug 473009. + gRunFiltersFolder._folder = aFolder; + // Calling this should go away in bug 802609. + gRunFiltersFolder.menupopup.selectFolder(gRunFiltersFolder._folder); + updateButtons(); +} + +/** + * Toggle enabled state of a filter, in both the filter properties and the UI. + * + * @param aFilterItem an item (row) of the filter list to be toggled + */ +function toggleFilter(aFilterItem, aSetForEvent) { + let filter = aFilterItem._filter; + if (filter.unparseable && !filter.enabled) { + Services.prompt.alert( + window, + null, + gFilterBundle.getFormattedString("cannotEnableIncompatFilter", [ + document.getElementById("bundle_brand").getString("brandShortName"), + ]) + ); + return; + } + filter.enabled = aSetForEvent === undefined ? !filter.enabled : aSetForEvent; + + // Now update the checkbox + if (aSetForEvent === undefined) { + aFilterItem.firstElementChild.nextElementSibling.checked = filter.enabled; + } + // For accessibility set the checked state on listitem + aFilterItem.setAttribute("aria-checked", filter.enabled); +} + +/** + * Selects a specific filter in the filter list. + * The listbox view is scrolled to the corresponding item. + * + * @param aFilter The nsIMsgFilter to select. + * + * @returns true/false indicating whether the filter was found and selected. + */ +function selectFilter(aFilter) { + if (currentFilter() == aFilter) { + return true; + } + + resetSearchBox(aFilter); + + let filterCount = gCurrentFilterList.filterCount; + for (let i = 0; i < filterCount; i++) { + if (gCurrentFilterList.getFilterAt(i) == aFilter) { + gFilterListbox.ensureIndexIsVisible(i); + gFilterListbox.selectedIndex = i; + return true; + } + } + return false; +} + +/** + * Returns the currently selected filter. If multiple filters are selected, + * returns the first one. If none are selected, returns null. + */ +function currentFilter() { + let currentItem = gFilterListbox.selectedItem; + return currentItem ? currentItem._filter : null; +} + +function onEditFilter() { + if (gEditButton.disabled) { + return; + } + + let selectedFilter = currentFilter(); + if (!selectedFilter) { + return; + } + + let args = { filter: selectedFilter, filterList: gCurrentFilterList }; + + window.openDialog( + "chrome://messenger/content/FilterEditor.xhtml", + "FilterEditor", + "chrome,modal,titlebar,resizable,centerscreen", + args + ); + + if ("refresh" in args && args.refresh) { + // reset search if edit was okay (name change might lead to hidden entry!) + resetSearchBox(selectedFilter); + rebuildFilterList(); + } +} + +/** + * Handler function for the 'New...' buttons. + * Opens the filter dialog for creating a new filter. + */ +function onNewFilter() { + calculatePositionAndShowCreateFilterDialog({}); +} + +/** + * Handler function for the 'Copy...' button. + * Opens the filter dialog for copying the selected filter. + */ +function onCopyToNewFilter() { + if (gCopyToNewButton.disabled) { + return; + } + + let selectedFilter = currentFilter(); + if (!selectedFilter) { + return; + } + + let args = { copiedFilter: selectedFilter }; + + calculatePositionAndShowCreateFilterDialog(args); +} + +/** + * Calculates the position for inserting the new filter, + * and then displays the create dialog. + * + * @param args The object containing the arguments for the dialog, + * passed to the filterEditorOnLoad() function. + * It will be augmented with the insertion position + * and global filters list properties by this function. + */ +function calculatePositionAndShowCreateFilterDialog(args) { + let selectedFilter = currentFilter(); + // If no filter is selected use the first position. + let position = 0; + if (selectedFilter) { + // Get the position in the unfiltered list. + // - this is where the new filter should be inserted! + let filterCount = gCurrentFilterList.filterCount; + for (let i = 0; i < filterCount; i++) { + if (gCurrentFilterList.getFilterAt(i) == selectedFilter) { + position = i; + break; + } + } + } + args.filterPosition = position; + + args.filterList = gCurrentFilterList; + + window.openDialog( + "chrome://messenger/content/FilterEditor.xhtml", + "FilterEditor", + "chrome,modal,titlebar,resizable,centerscreen", + args + ); + + if ("refresh" in args && args.refresh) { + // On success: reset the search box if necessary! + resetSearchBox(args.newFilter); + rebuildFilterList(); + + // Select the new filter, it is at the position of previous selection. + gFilterListbox.selectItem(gFilterListbox.getItemAtIndex(position)); + if (currentFilter() != args.newFilter) { + console.error("Filter created at an unexpected position!"); + } + } +} + +/** + * Delete selected filters. + * 'Selected' is not to be confused with active (checkbox checked) + */ +function onDeleteFilter() { + if (gDeleteButton.disabled) { + return; + } + + let items = gFilterListbox.selectedItems; + if (!items.length) { + return; + } + + let checkValue = { value: false }; + if ( + Services.prefs.getBoolPref("mailnews.filters.confirm_delete") && + Services.prompt.confirmEx( + window, + null, + gFilterBundle.getString("deleteFilterConfirmation"), + Services.prompt.STD_YES_NO_BUTTONS, + "", + "", + "", + gFilterBundle.getString("dontWarnAboutDeleteCheckbox"), + checkValue + ) + ) { + return; + } + + if (checkValue.value) { + Services.prefs.setBoolPref("mailnews.filters.confirm_delete", false); + } + + // Save filter position before the first selected one. + let newSelectionIndex = gFilterListbox.selectedIndex - 1; + + // Must reverse the loop, as the items list shrinks when we delete. + for (let index = items.length - 1; index >= 0; --index) { + let item = items[index]; + gCurrentFilterList.removeFilter(item._filter); + item.remove(); + } + updateCountBox(); + + // Select filter above previously selected if one existed, otherwise the first one. + if (newSelectionIndex == -1 && gFilterListbox.itemCount > 0) { + newSelectionIndex = 0; + } + if (newSelectionIndex > -1) { + gFilterListbox.selectedIndex = newSelectionIndex; + updateViewPosition(-1); + } +} + +/** + * Move filter one step up in visible list. + */ +function onUp(event) { + moveFilter(msgMoveMotion.Up); +} + +/** + * Move filter one step down in visible list. + */ +function onDown(event) { + moveFilter(msgMoveMotion.Down); +} + +/** + * Move filter to bottom for long filter lists. + */ +function onTop(evt) { + moveFilter(msgMoveMotion.Top); +} + +/** + * Move filter to top for long filter lists. + */ +function onBottom(evt) { + moveFilter(msgMoveMotion.Bottom); +} + +/** + * Moves a singular selected filter up or down either 1 increment or to the + * top/bottom. This acts on the visible filter list only which means that: + * + * - when moving up or down "1" the filter may skip one or more other + * filters (which are currently not visible) - this will also lead + * to the "related" filters (e.g search filters containing 'moz') + * being grouped more closely together + * - moveTop / moveBottom + * this is currently moving to the top/bottom of the absolute list + * but it would be better if it moved "just as far as necessary" + * which would further "compact" related filters + * + * @param motion + * msgMoveMotion.Up, msgMoveMotion.Down, msgMoveMotion.Top, msgMoveMotion.Bottom + */ +function moveFilter(motion) { + // At the moment, do not allow moving groups of filters. + let selectedFilter = currentFilter(); + if (!selectedFilter) { + return; + } + + var relativeStep = 0; + var moveFilterNative = null; + + switch (motion) { + case msgMoveMotion.Top: + if (selectedFilter) { + gCurrentFilterList.removeFilter(selectedFilter); + gCurrentFilterList.insertFilterAt(0, selectedFilter); + rebuildFilterList(); + } + return; + case msgMoveMotion.Bottom: + if (selectedFilter) { + gCurrentFilterList.removeFilter(selectedFilter); + gCurrentFilterList.insertFilterAt( + gCurrentFilterList.filterCount, + selectedFilter + ); + rebuildFilterList(); + } + return; + case msgMoveMotion.Up: + relativeStep = -1; + moveFilterNative = Ci.nsMsgFilterMotion.up; + break; + case msgMoveMotion.Down: + relativeStep = +1; + moveFilterNative = Ci.nsMsgFilterMotion.down; + break; + } + + if (!gSearchBox.value) { + // use legacy move filter code: up, down; only if searchBox is empty + moveCurrentFilter(moveFilterNative); + return; + } + + let nextIndex = gFilterListbox.selectedIndex + relativeStep; + let nextFilter = gFilterListbox.getItemAtIndex(nextIndex)._filter; + + gCurrentFilterList.removeFilter(selectedFilter); + + // Find the index of the filter we want to insert at. + let newIndex = -1; + let filterCount = gCurrentFilterList.filterCount; + for (let i = 0; i < filterCount; i++) { + if (gCurrentFilterList.getFilterAt(i) == nextFilter) { + newIndex = i; + break; + } + } + + if (motion == msgMoveMotion.Down) { + newIndex += relativeStep; + } + + gCurrentFilterList.insertFilterAt(newIndex, selectedFilter); + + rebuildFilterList(); +} + +function viewLog() { + var args = { filterList: gCurrentFilterList }; + + window.openDialog( + "chrome://messenger/content/viewLog.xhtml", + "FilterLog", + "chrome,modal,titlebar,resizable,centerscreen", + args + ); +} + +function onFilterUnload() { + gCurrentFilterList.saveToDefaultFile(); + Services.obs.removeObserver( + filterEditorQuitObserver, + "quit-application-requested" + ); + + gFilterListMsgWindow.closeWindow(); +} + +function onFilterClose() { + if ( + gRunFiltersButton.getAttribute("label") == + gRunFiltersButton.getAttribute("stoplabel") + ) { + let promptTitle = gFilterBundle.getString("promptTitle"); + let promptMsg = gFilterBundle.getString("promptMsg"); + let stopButtonLabel = gFilterBundle.getString("stopButtonLabel"); + let continueButtonLabel = gFilterBundle.getString("continueButtonLabel"); + + let result = Services.prompt.confirmEx( + window, + promptTitle, + promptMsg, + Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 + + Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1, + continueButtonLabel, + stopButtonLabel, + null, + null, + { value: 0 } + ); + + if (result) { + gFilterListMsgWindow.StopUrls(); + } else { + return false; + } + } + + return true; +} + +function runSelectedFilters() { + // if run button has "stop" label, do stop. + if ( + gRunFiltersButton.getAttribute("label") == + gRunFiltersButton.getAttribute("stoplabel") + ) { + gFilterListMsgWindow.StopUrls(); + return; + } + + let folder = + gRunFiltersFolder._folder || gRunFiltersFolder.selectedItem._folder; + if (!folder) { + return; + } + + let filterList = MailServices.filters.getTempFilterList(folder); + + // make sure the tmp filter list uses the real filter list log stream + filterList.loggingEnabled = gCurrentFilterList.loggingEnabled; + filterList.logStream = gCurrentFilterList.logStream; + + let index = 0; + for (let item of gFilterListbox.selectedItems) { + filterList.insertFilterAt(index++, item._filter); + } + + MailServices.filters.applyFiltersToFolders( + filterList, + [folder], + gFilterListMsgWindow + ); +} + +function moveCurrentFilter(motion) { + let filter = currentFilter(); + if (!filter) { + return; + } + + gCurrentFilterList.moveFilter(filter, motion); + rebuildFilterList(); +} + +/** + * Redraws the list of filters. Takes the search box value into account. + * + * This function should perform very fast even in case of high number of filters. + * Therefore there are some optimizations (e.g. listelement.itemChildren[] instead of + * list.getItemAtIndex()), that favour speed vs. semantical perfection. + */ +function rebuildFilterList() { + // Get filters that match the search box. + let aTempFilterList = onFindFilter(); + + let searchBoxFocus = false; + let activeElement = document.activeElement; + + // Find if the currently focused element is a child inside the search box + // (probably html:input). Traverse up the parents until the first element + // with an ID is found. If it is not searchBox, return false. + while (activeElement != null) { + if (activeElement == gSearchBox) { + searchBoxFocus = true; + break; + } else if (activeElement.id) { + searchBoxFocus = false; + break; + } + activeElement = activeElement.parentNode; + } + + // Make a note of which filters were previously selected + let selectedNames = []; + for (let i = 0; i < gFilterListbox.selectedItems.length; i++) { + selectedNames.push(gFilterListbox.selectedItems[i]._filter.filterName); + } + + // Save scroll position so we can try to restore it later. + // Doesn't work when the list is rebuilt after search box condition changed. + let firstVisibleRowIndex = gFilterListbox.getIndexOfFirstVisibleRow(); + + // listbox.xml seems to cache the value of the first selected item in a + // range at _selectionStart. The old value though is now obsolete, + // since we will recreate all of the elements. We need to clear this, + // and one way to do this is with a call to clearSelection. This might be + // ugly from an accessibility perspective, since it fires an onSelect event. + gFilterListbox.clearSelection(); + + let listitem, nameCell, enabledCell, filter; + let filterCount = gCurrentFilterList.filterCount; + let listitemCount = gFilterListbox.itemCount; + let listitemIndex = 0; + let tempFilterListLength = aTempFilterList ? aTempFilterList.length - 1 : 0; + for (let i = 0; i < filterCount; i++) { + if (aTempFilterList && listitemIndex > tempFilterListLength) { + break; + } + + filter = gCurrentFilterList.getFilterAt(i); + if (aTempFilterList && aTempFilterList[listitemIndex] != i) { + continue; + } + + if (listitemCount > listitemIndex) { + // If there is a free existing listitem, reuse it. + // Use .itemChildren[] instead of .getItemAtIndex() as it is much faster. + listitem = gFilterListbox.itemChildren[listitemIndex]; + nameCell = listitem.firstElementChild; + enabledCell = nameCell.nextElementSibling; + } else { + // If there are not enough listitems in the list, create a new one. + listitem = document.createXULElement("richlistitem"); + listitem.setAttribute("align", "center"); + listitem.setAttribute("role", "checkbox"); + nameCell = document.createXULElement("label"); + nameCell.setAttribute("flex", "1"); + nameCell.setAttribute("crop", "end"); + enabledCell = document.createXULElement("checkbox"); + enabledCell.setAttribute("style", "padding-inline-start: 25px;"); + enabledCell.addEventListener("CheckboxStateChange", onFilterClick, true); + listitem.appendChild(nameCell); + listitem.appendChild(enabledCell); + gFilterListbox.appendChild(listitem); + // We have to attach this listener to the listitem, even though we only care + // about clicks on the enabledCell. However, attaching to that item doesn't + // result in any events actually getting received. + listitem.addEventListener("dblclick", onFilterDoubleClick, true); + } + // For accessibility set the label on listitem. + listitem.setAttribute("label", filter.filterName); + // Set the listitem values to represent the current filter. + nameCell.setAttribute("value", filter.filterName); + if (filter.enabled) { + enabledCell.setAttribute("checked", "true"); + } else { + enabledCell.removeAttribute("checked"); + } + listitem.setAttribute("aria-checked", filter.enabled); + listitem._filter = filter; + + if (selectedNames.includes(filter.filterName)) { + gFilterListbox.addItemToSelection(listitem); + } + + listitemIndex++; + } + // Remove any superfluous listitems, if the number of filters shrunk. + for (let i = listitemCount - 1; i >= listitemIndex; i--) { + gFilterListbox.lastChild.remove(); + } + + updateViewPosition(firstVisibleRowIndex); + updateCountBox(); + + // If before rebuilding the list the searchbox was focused, focus it again. + // In any other case, focus the list. + if (searchBoxFocus) { + gSearchBox.focus(); + } else { + gFilterListbox.focus(); + } +} + +function updateViewPosition(firstVisibleRowIndex) { + if (firstVisibleRowIndex == -1) { + firstVisibleRowIndex = gFilterListbox.getIndexOfFirstVisibleRow(); + } + + // Restore to the extent possible the scroll position. + if (firstVisibleRowIndex && gFilterListbox.itemCount) { + gFilterListbox.ensureElementIsVisible( + gFilterListbox.getItemAtIndex( + Math.min(firstVisibleRowIndex, gFilterListbox.itemCount - 1) + ), + true + ); + } + + if (gFilterListbox.selectedCount) { + // Make sure that at least the first selected item is visible. + gFilterListbox.ensureElementIsVisible(gFilterListbox.selectedItems[0]); + + // The current item should be the first selected item, so that keyboard + // selection extension can work. + gFilterListbox.currentItem = gFilterListbox.selectedItems[0]; + } + + updateButtons(); +} + +/** + * Try to only enable buttons that make sense + * - moving filters is currently only enabled for single selection + * also movement is restricted by searchBox and current selection position + * - edit only for single filters + * - delete / run only for one or more selected filters + */ +function updateButtons() { + var numFiltersSelected = gFilterListbox.selectedItems.length; + var oneFilterSelected = numFiltersSelected == 1; + + // "edit" is disabled when not exactly one filter is selected + // or if we couldn't parse that filter + let disabled = !oneFilterSelected || currentFilter().unparseable; + gEditButton.disabled = disabled; + + // "copy" is the same as "edit" + gCopyToNewButton.disabled = disabled; + + // "delete" only disabled when no filters are selected + gDeleteButton.disabled = !numFiltersSelected; + + // we can run multiple filters on a folder + // so only disable this UI if no filters are selected + document.getElementById("folderPickerPrefix").disabled = !numFiltersSelected; + gRunFiltersFolder.disabled = !numFiltersSelected; + gRunFiltersButton.disabled = + !numFiltersSelected || !gRunFiltersFolder._folder; + // "up" and "top" enabled only if one filter is selected, and it's not the first + // don't use gFilterListbox.currentIndex here, it's buggy when we've just changed the + // children in the list (via rebuildFilterList) + disabled = !( + oneFilterSelected && + gFilterListbox.getSelectedItem(0) != gFilterListbox.getItemAtIndex(0) + ); + gUpButton.disabled = disabled; + gTopButton.disabled = disabled; + + // "down" and "bottom" enabled only if one filter is selected, + // and it's not the last one + disabled = !( + oneFilterSelected && + gFilterListbox.selectedIndex < gFilterListbox.itemCount - 1 + ); + gDownButton.disabled = disabled; + gBottomButton.disabled = disabled; +} + +/** + * Given a selected folder, returns the folder where filters should + * be defined (the root folder except for news) if the server can + * accept filters. + * + * @param nsIMsgFolder aFolder - selected folder, from window args + * @returns an nsIMsgFolder where the filter is defined + */ +function getFilterFolderForSelection(aFolder) { + let rootFolder = aFolder && aFolder.server ? aFolder.server.rootFolder : null; + if (rootFolder && rootFolder.isServer && rootFolder.server.canHaveFilters) { + return aFolder.server.type == "nntp" ? aFolder : rootFolder; + } + + return null; +} + +/** + * If the selected server cannot have filters, get the default server. + * If the default server cannot have filters, check all accounts + * and get a server that can have filters. + * + * @returns an nsIMsgIncomingServer + */ +function getServerThatCanHaveFilters() { + let defaultAccount = MailServices.accounts.defaultAccount; + if (defaultAccount) { + let defaultIncomingServer = defaultAccount.incomingServer; + // Check to see if default server can have filters. + if (defaultIncomingServer.canHaveFilters) { + return defaultIncomingServer; + } + } + + // If it cannot, check all accounts to find a server + // that can have filters. + return MailServices.accounts.allServers.find(server => server.canHaveFilters); +} + +function onFilterClick(event) { + // This is called after the clicked checkbox changed state + // so this.checked is the right state we want to toggle to. + toggleFilter(this.parentNode, this.checked); +} + +function onFilterDoubleClick(event) { + // we only care about button 0 (left click) events + if (event.button != 0) { + return; + } + + onEditFilter(); +} + +/** + * Handles the keypress event on the filter list dialog. + * + * @param {Event} event - The keypress DOMEvent. + */ +function onFilterActionButtonKeyPress(event) { + if ( + event.key == "Enter" || + (event.key == " " && event.target.hasAttribute("type")) + ) { + event.preventDefault(); + + if ( + event.target.classList.contains("toolbarbutton-menubutton-dropmarker") + ) { + document + .getElementById("newFilterMenupopup") + .openPopup(event.target.parentNode, "after_end", { + triggerEvent: event, + }); + return; + } + event.target.click(); + } +} + +function onFilterListKeyPress(aEvent) { + if (aEvent.keyCode) { + switch (aEvent.keyCode) { + case KeyEvent.DOM_VK_INSERT: + if (!document.getElementById("newButton").disabled) { + onNewFilter(); + } + break; + case KeyEvent.DOM_VK_DELETE: + if (!document.getElementById("deleteButton").disabled) { + onDeleteFilter(); + } + break; + case KeyEvent.DOM_VK_RETURN: + if (!document.getElementById("editButton").disabled) { + onEditFilter(); + } + break; + } + } else if (!aEvent.ctrlKey && !aEvent.altKey && !aEvent.metaKey) { + switch (aEvent.charCode) { + case KeyEvent.DOM_VK_SPACE: + for (let item of gFilterListbox.selectedItems) { + toggleFilter(item); + } + break; + default: + gSearchBox.focus(); + gSearchBox.value = String.fromCharCode(aEvent.charCode); + } + } +} + +/** + * Decides if the given filter matches the given keyword. + * + * @param aFilter nsIMsgFilter to check + * @param aKeyword the string to find in the filter name + * + * @returns True if the filter name contains the searched keyword. + Otherwise false. In the future this may be extended to match + other filter attributes. + */ +function filterSearchMatch(aFilter, aKeyword) { + return aFilter.filterName.toLocaleLowerCase().includes(aKeyword); +} + +/** + * Called from rebuildFilterList when the list needs to be redrawn. + * + * @returns Uses the search term in search box, to produce an array of + * row (filter) numbers (indexes) that match the search term. + */ +function onFindFilter() { + let keyWord = gSearchBox.value.toLocaleLowerCase(); + + // If searchbox is empty, just return and let rebuildFilterList + // create an unfiltered list. + if (!keyWord) { + return null; + } + + // Rematch everything in the list, remove what doesn't match the search box. + let rows = gCurrentFilterList.filterCount; + let matchingFilterList = []; + // Use the full gCurrentFilterList, not the filterList listbox, + // which may already be filtered. + for (let i = 0; i < rows; i++) { + if (filterSearchMatch(gCurrentFilterList.getFilterAt(i), keyWord)) { + matchingFilterList.push(i); + } + } + + return matchingFilterList; +} + +/** + * Clear the search term in the search box if needed. + * + * @param aFilter If this nsIMsgFilter matches the search term, + * do not reset the box. If this is null, + * reset unconditionally. + */ +function resetSearchBox(aFilter) { + let keyword = gSearchBox.value.toLocaleLowerCase(); + if (keyword && (!aFilter || !filterSearchMatch(aFilter, keyword))) { + gSearchBox.reset(); + } +} + +/** + * Display "1 item", "11 items" or "4 of 10" if list is filtered via search box. + */ +function updateCountBox() { + let countBox = document.getElementById("countBox"); + let sum = gCurrentFilterList.filterCount; + let len = gFilterListbox.itemCount; + + if (len == sum) { + // "N items" + countBox.value = PluralForm.get( + len, + gFilterBundle.getString("filterCountItems") + ).replace("#1", len); + return; + } + + // "N of M" + countBox.value = gFilterBundle.getFormattedString( + "filterCountVisibleOfTotal", + [len, sum] + ); +} diff --git a/comm/mail/base/content/FilterListDialog.xhtml b/comm/mail/base/content/FilterListDialog.xhtml new file mode 100644 index 0000000000..397bdea0d8 --- /dev/null +++ b/comm/mail/base/content/FilterListDialog.xhtml @@ -0,0 +1,168 @@ + + + + + + + + + + + + +%filtersDTD; +]> + + + &window.title; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +