/** * 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/. */ /* import-globals-from ../../../mailnews/base/prefs/content/accountUtils.js */ /* import-globals-from ../../components/addrbook/content/addressBookTab.js */ /* import-globals-from ../../components/customizableui/content/panelUI.js */ /* import-globals-from ../../components/newmailaccount/content/provisionerCheckout.js */ /* import-globals-from ../../components/preferences/preferencesTab.js */ /* import-globals-from glodaFacetTab.js */ /* import-globals-from mailCore.js */ /* import-globals-from mail-offline.js */ /* import-globals-from mailTabs.js */ /* import-globals-from mailWindowOverlay.js */ /* import-globals-from messenger-customization.js */ /* import-globals-from searchBar.js */ /* import-globals-from spacesToolbar.js */ /* import-globals-from specialTabs.js */ /* import-globals-from toolbarIconColor.js */ /* globals CreateMailWindowGlobals, InitMsgWindow, OnMailWindowUnload */ // From mailWindow.js /* globals loadCalendarComponent */ ChromeUtils.import("resource:///modules/activity/activityModules.jsm"); var { MailServices } = ChromeUtils.import( "resource:///modules/MailServices.jsm" ); var { AppConstants } = ChromeUtils.importESModule( "resource://gre/modules/AppConstants.sys.mjs" ); ChromeUtils.defineESModuleGetters(this, { Color: "resource://gre/modules/Color.sys.mjs", ctypes: "resource://gre/modules/ctypes.sys.mjs", }); XPCOMUtils.defineLazyModuleGetters(this, { BondOpenPGP: "chrome://openpgp/content/BondOpenPGP.jsm", MailConsts: "resource:///modules/MailConsts.jsm", MailUtils: "resource:///modules/MailUtils.jsm", msgDBCacheManager: "resource:///modules/MsgDBCacheManager.jsm", PeriodicFilterManager: "resource:///modules/PeriodicFilterManager.jsm", SessionStoreManager: "resource:///modules/SessionStoreManager.jsm", }); XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () { let { PopupNotifications } = ChromeUtils.import( "resource:///modules/GlobalPopupNotifications.jsm" ); try { // Hide all notifications while the URL is being edited and the address bar // has focus, including the virtual focus in the results popup. // We also have to hide notifications explicitly when the window is // minimized because of the effects of the "noautohide" attribute on Linux. // This can be removed once bug 545265 and bug 1320361 are fixed. let shouldSuppress = () => window.windowState == window.STATE_MINIMIZED; return new PopupNotifications( document.getElementById("tabmail"), document.getElementById("notification-popup"), document.getElementById("notification-popup-box"), { shouldSuppress } ); } catch (ex) { console.error(ex); return null; } }); /** * Gets the service pack and build information on Windows platforms. The initial version * was copied from nsUpdateService.js. * * @returns An object containing the service pack major and minor versions, along with the * build number. */ function getWindowsVersionInfo() { const UNKNOWN_VERSION_INFO = { servicePackMajor: null, servicePackMinor: null, buildNumber: null, }; if (AppConstants.platform !== "win") { return UNKNOWN_VERSION_INFO; } const BYTE = ctypes.uint8_t; const WORD = ctypes.uint16_t; const DWORD = ctypes.uint32_t; const WCHAR = ctypes.char16_t; const BOOL = ctypes.int; // This structure is described at: // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx const SZCSDVERSIONLENGTH = 128; const OSVERSIONINFOEXW = new ctypes.StructType("OSVERSIONINFOEXW", [ { dwOSVersionInfoSize: DWORD }, { dwMajorVersion: DWORD }, { dwMinorVersion: DWORD }, { dwBuildNumber: DWORD }, { dwPlatformId: DWORD }, { szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH) }, { wServicePackMajor: WORD }, { wServicePackMinor: WORD }, { wSuiteMask: WORD }, { wProductType: BYTE }, { wReserved: BYTE }, ]); let kernel32 = ctypes.open("kernel32"); try { let GetVersionEx = kernel32.declare( "GetVersionExW", ctypes.winapi_abi, BOOL, OSVERSIONINFOEXW.ptr ); let winVer = OSVERSIONINFOEXW(); winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size; if (0 === GetVersionEx(winVer.address())) { throw new Error("Failure in GetVersionEx (returned 0)"); } return { servicePackMajor: winVer.wServicePackMajor, servicePackMinor: winVer.wServicePackMinor, buildNumber: winVer.dwBuildNumber, }; } catch (e) { return UNKNOWN_VERSION_INFO; } finally { kernel32.close(); } } /* This is where functions related to the 3 pane window are kept */ // from MailNewsTypes.h var kMailCheckOncePrefName = "mail.startup.enabledMailCheckOnce"; /** * Tracks whether the right mouse button changed the selection or not. If the * user right clicks on the selection, it stays the same. If they click outside * of it, we alter the selection (but not the current index) to be the row they * clicked on. * * The value of this variable is an object with "view" and "selection" keys * and values. The view value is the view whose selection we saved off, and * the selection value is the selection object we saved off. */ var gRightMouseButtonSavedSelection = null; var gNewAccountToLoad = null; // The object in charge of managing the mail summary pane var gSummaryFrameManager; /** * Called on startup if there are no accounts. */ function verifyOpenAccountHubTab() { let suppressDialogs = Services.prefs.getBoolPref( "mail.provider.suppress_dialog_on_startup", false ); if (suppressDialogs) { // Looks like we were in the middle of filling out an account form. We // won't display the dialogs in that case. Services.prefs.clearUserPref("mail.provider.suppress_dialog_on_startup"); loadPostAccountWizard(); return; } openAccountSetupTab(); } let _resolveDelayedStartup; var delayedStartupPromise = new Promise(resolve => { _resolveDelayedStartup = resolve; }); var gMailInit = { onBeforeInitialXULLayout() { // Set a sane starting width/height for all resolutions on new profiles. // Do this before the window loads. if (!document.documentElement.hasAttribute("width")) { const TARGET_WIDTH = 1280; let defaultWidth = Math.min(screen.availWidth * 0.9, TARGET_WIDTH); let defaultHeight = screen.availHeight; document.documentElement.setAttribute("width", defaultWidth); document.documentElement.setAttribute("height", defaultHeight); // On small screens, default to maximized state. if (defaultWidth < TARGET_WIDTH) { document.documentElement.setAttribute("sizemode", "maximized"); } // Make sure we're safe at the left/top edge of screen document.documentElement.setAttribute("screenX", screen.availLeft); document.documentElement.setAttribute("screenY", screen.availTop); } // Run menubar initialization first, to avoid TabsInTitlebar code picking // up mutations from it and causing a reflow. AutoHideMenubar.init(); TabsInTitlebar.init(); if (AppConstants.platform == "win") { // On Win8 set an attribute when the window frame color is too dark for black text. if ( window.matchMedia("(-moz-platform: windows-win8)").matches && window.matchMedia("(-moz-windows-default-theme)").matches ) { let { Windows8WindowFrameColor } = ChromeUtils.importESModule( "resource:///modules/Windows8WindowFrameColor.sys.mjs" ); let windowFrameColor = new Color(...Windows8WindowFrameColor.get()); // Default to black for foreground text. if (!windowFrameColor.isContrastRatioAcceptable(new Color(0, 0, 0))) { document.documentElement.setAttribute("darkwindowframe", "true"); } } else if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) { // 17763 is the build number of Windows 10 version 1809 if (getWindowsVersionInfo().buildNumber < 17763) { document.documentElement.setAttribute( "always-use-accent-color-for-window-border", "" ); } } } // Call this after we set attributes that might change toolbars' computed // text color. ToolbarIconColor.init(); }, /** * Called on startup to initialize various parts of the main window. * Most of this should be moved out into _delayedStartup or only * initialized when needed. */ onLoad() { CreateMailWindowGlobals(); if (!Services.policies.isAllowed("devtools")) { let devtoolsMenu = document.getElementById("devtoolsMenu"); if (devtoolsMenu) { devtoolsMenu.hidden = true; } } // - initialize tabmail system // Do this before loadPostAccountWizard since that code selects the first // folder for display, and we want gFolderDisplay setup and ready to handle // that event chain. // Also, we definitely need to register the tab type prior to the call to // specialTabs.openSpecialTabsOnStartup below. let tabmail = document.getElementById("tabmail"); if (tabmail) { // mailTabType is defined in mailTabs.js tabmail.registerTabType(mailTabType); // glodaFacetTab* in glodaFacetTab.js tabmail.registerTabType(glodaFacetTabType); tabmail.registerTabMonitor(GlodaSearchBoxTabMonitor); tabmail.openFirstTab(); } // This also registers the contentTabType ("contentTab") specialTabs.openSpecialTabsOnStartup(); tabmail.registerTabType(addressBookTabType); tabmail.registerTabType(preferencesTabType); // provisionerCheckoutTabType is defined in provisionerCheckout.js tabmail.registerTabType(provisionerCheckoutTabType); // Depending on the pref, hide/show the gloda toolbar search widgets. XPCOMUtils.defineLazyPreferenceGetter( this, "gGlodaEnabled", "mailnews.database.global.indexer.enabled", true, (pref, oldVal, newVal) => { for (let widget of document.querySelectorAll(".gloda-search-widget")) { widget.hidden = !newVal; } } ); for (let widget of document.querySelectorAll(".gloda-search-widget")) { widget.hidden = !this.gGlodaEnabled; } window.addEventListener("AppCommand", HandleAppCommandEvent, true); this._boundDelayedStartup = this._delayedStartup.bind(this); window.addEventListener("MozAfterPaint", this._boundDelayedStartup); // Listen for the messages sent to the main 3 pane window. window.addEventListener("message", this._onMessageReceived); }, _cancelDelayedStartup() { window.removeEventListener("MozAfterPaint", this._boundDelayedStartup); this._boundDelayedStartup = null; }, /** * Handle the messages sent via postMessage() method to the main 3 pane * window. * * @param {Event} event - The message event. */ _onMessageReceived(event) { switch (event.data) { case "account-created": case "account-created-in-backend": case "account-created-from-provisioner": // Set the pref to false in case it was previously changed. Services.prefs.setBoolPref("app.use_without_mail_account", false); loadPostAccountWizard(); // Always update the mail UI to guarantee all the panes are visible even // if the mail tab is not the currently active tab. updateMailPaneUI(); break; case "account-setup-closed": // The user closed the account setup after a successful run. Make sure // to focus on the primary mail tab. switchToMailTab(); gSpacesToolbar.onLoad(); // Trigger the integration dialog if necessary. showSystemIntegrationDialog(); break; case "account-setup-dismissed": // The user closed the account setup before completing it. Be sure to // initialize the few important areas we need. if (!gSpacesToolbar.isLoaded) { loadPostAccountWizard(); } break; case "open-account-setup-tab": openAccountSetupTab(); break; default: break; } }, /** * Delayed startup happens after the first paint of the window. Anything * that can be delayed until after paint, should be to help give the * illusion that Thunderbird is starting faster. * * Note: this only runs for the main 3 pane window. */ _delayedStartup() { this._cancelDelayedStartup(); MailOfflineMgr.init(); BondOpenPGP.init(); PanelUI.init(); gExtensionsNotifications.init(); Services.search.init(); PeriodicFilterManager.setupFiltering(); msgDBCacheManager.init(); this.delayedStartupFinished = true; _resolveDelayedStartup(window); Services.obs.notifyObservers(window, "browser-delayed-startup-finished"); // Notify observer to resolve the browserStartupPromise, which is used for the // delayed background startup of WebExtensions. Services.obs.notifyObservers(window, "extensions-late-startup"); this._loadComponentsAtStartup(); }, /** * Load all the necessary components to make Thunderbird usable before * checking for existing accounts. */ async _loadComponentsAtStartup() { updateTroubleshootMenuItem(); // The calendar component needs to be loaded before restoring any tabs. await loadCalendarComponent(); // Don't trigger the existing account verification if the user wants to use // Thunderbird without an email account. if (!Services.prefs.getBoolPref("app.use_without_mail_account", false)) { // Load the Mail UI only if we already have at least one account configured // otherwise the verifyExistingAccounts will trigger the account wizard. if (verifyExistingAccounts()) { switchToMailTab(); await loadPostAccountWizard(); } } else { // Run the tabs restore method here since we're skipping the loading of // the Mail UI which would have taken care of this to properly handle // opened folders or messages in tabs. await atStartupRestoreTabs(false); gSpacesToolbar.onLoad(); } // Show the end of year donation appeal page. if (this.shouldShowEOYDonationAppeal()) { // Add a timeout to prevent opening the browser immediately at startup. setTimeout(this.showEOYDonationAppeal, 2000); } }, /** * Called by messenger.xhtml:onunload, the 3-pane window inside of tabs window. * It's being unloaded! Right now! */ onUnload() { Services.obs.notifyObservers(window, "mail-unloading-messenger"); if (gRightMouseButtonSavedSelection) { // Avoid possible cycle leaks. gRightMouseButtonSavedSelection.view = null; gRightMouseButtonSavedSelection = null; } SessionStoreManager.unloadingWindow(window); TabsInTitlebar.uninit(); ToolbarIconColor.uninit(); gSpacesToolbar.onUnload(); document.getElementById("tabmail")._teardown(); OnMailWindowUnload(); }, /** * Check if we can trigger the opening of the donation appeal page. * * @returns {boolean} - True if the donation appeal page should be opened. */ shouldShowEOYDonationAppeal() { let currentEOY = Services.prefs.getIntPref("app.donation.eoy.version", 1); let viewedEOY = Services.prefs.getIntPref( "app.donation.eoy.version.viewed", 0 ); // True if the user never saw the donation appeal, this is not a new // profile (since users are already prompted to donate after downloading), // and we're not running tests. return ( viewedEOY < currentEOY && !specialTabs.shouldShowPolicyNotification() && !Cu.isInAutomation ); }, /** * Open the end of year appeal in a new web browser page. We don't open this * in a tab due to the complexity of the donation site, and we don't want to * handle that inside Thunderbird. */ showEOYDonationAppeal() { let url = Services.prefs.getStringPref("app.donation.eoy.url"); let protocolSvc = Cc[ "@mozilla.org/uriloader/external-protocol-service;1" ].getService(Ci.nsIExternalProtocolService); protocolSvc.loadURI(Services.io.newURI(url)); let currentEOY = Services.prefs.getIntPref("app.donation.eoy.version", 1); Services.prefs.setIntPref("app.donation.eoy.version.viewed", currentEOY); }, }; /** * Called at startup to verify if we have ny existing account, even if invalid, * and if not, it will trigger the Account Hub in a tab. * * @returns {boolean} - True if we have at least one existing account. */ function verifyExistingAccounts() { try { // Migrate quoting preferences from global to per account. This function // returns true if it had to migrate, which we will use to mean this is a // just migrated or new profile. let newProfile = migrateGlobalQuotingPrefs( MailServices.accounts.allIdentities ); // If there are no accounts, or all accounts are "invalid" then kick off the // account migration. Or if this is a new (to Mozilla) profile. MCD can set // up accounts without the profile being used yet. if (newProfile) { // Check if MCD is configured. If not, say this is not a new profile so // that we don't accidentally remigrate non MCD profiles. var adminUrl = Services.prefs.getCharPref( "autoadmin.global_config_url", "" ); if (!adminUrl) { newProfile = false; } } let accounts = MailServices.accounts.accounts; let invalidAccounts = getInvalidAccounts(accounts); // Trigger the new account configuration wizard only if we don't have any // existing account, not even if we have at least one invalid account. if ( (newProfile && !accounts.length) || accounts.length == invalidAccounts.length || (invalidAccounts.length > 0 && invalidAccounts.length == accounts.length && invalidAccounts[0]) ) { verifyOpenAccountHubTab(); return false; } let localFoldersExists; try { localFoldersExists = MailServices.accounts.localFoldersServer; } catch (ex) { localFoldersExists = false; } // We didn't trigger the account configuration wizard, so we need to verify // that local folders exists. if (!localFoldersExists && requireLocalFoldersAccount()) { MailServices.accounts.createLocalMailAccount(); } return true; } catch (ex) { dump(`Error verifying accounts: ${ex}`); return false; } } /** * Switch the view to the first Mail tab if the currently selected tab is not * the first Mail tab. */ function switchToMailTab() { let tabmail = document.getElementById("tabmail"); if (tabmail?.selectedTab.mode.name != "folder") { tabmail.switchToTab(0); } } /** * Trigger the initialization of the entire UI. Called after the okCallback of * the emailWizard during a first run, or directly from the accountProvisioner * in case a user configures a new email account on first run. */ async function loadPostAccountWizard() { InitMsgWindow(); MigrateJunkMailSettings(); MigrateFolderViews(); MigrateOpenMessageBehavior(); MailServices.accounts.setSpecialFolders(); try { MailServices.accounts.loadVirtualFolders(); } catch (e) { console.error(e); } // Init the mozINewMailListener service (MailNotificationManager) before // any new mails are fetched. // MailNotificationManager triggers mozINewMailNotificationService // init as well. Cc["@mozilla.org/mail/notification-manager;1"].getService( Ci.mozINewMailListener ); // Restore the previous folder selection before shutdown, or select the first // inbox folder of a newly created account. await selectFirstFolder(); gSpacesToolbar.onLoad(); } /** * Check if we need to show the system integration dialog before notifying the * application that the startup process is completed. */ function showSystemIntegrationDialog() { // Check the shell service. let shellService; try { shellService = Cc["@mozilla.org/mail/shell-service;1"].getService( Ci.nsIShellService ); } catch (ex) {} let defaultAccount = MailServices.accounts.defaultAccount; // Load the search integration module. let { SearchIntegration } = ChromeUtils.import( "resource:///modules/SearchIntegration.jsm" ); // Show the default client dialog only if // EITHER: we have at least one account, and we aren't already the default // for mail, // OR: we have the search integration module, the OS version is suitable, // and the first run hasn't already been completed. // Needs to be shown outside the he normal load sequence so it doesn't appear // before any other displays, in the wrong place of the screen. if ( (shellService && defaultAccount && shellService.shouldCheckDefaultClient && !shellService.isDefaultClient(true, Ci.nsIShellService.MAIL)) || (SearchIntegration && !SearchIntegration.osVersionTooLow && !SearchIntegration.osComponentsNotRunning && !SearchIntegration.firstRunDone) ) { window.openDialog( "chrome://messenger/content/systemIntegrationDialog.xhtml", "SystemIntegration", "modal,centerscreen,chrome,resizable=no" ); // On Windows, there seems to be a delay between setting TB as the // default client, and the isDefaultClient check succeeding. if (shellService.isDefaultClient(true, Ci.nsIShellService.MAIL)) { Services.obs.notifyObservers(window, "mail:setAsDefault"); } } } /** * Properly select the starting folder or message header if we have one. */ async function selectFirstFolder() { let startFolderURI = null; let startMsgHdr = null; if ("arguments" in window && window.arguments.length > 0) { let arg0 = window.arguments[0]; // If the argument is a string, it is folder URI. if (typeof arg0 == "string") { startFolderURI = arg0; } else if (arg0) { // arg0 is an object if ("wrappedJSObject" in arg0 && arg0.wrappedJSObject) { arg0 = arg0.wrappedJSObject; } startMsgHdr = "msgHdr" in arg0 ? arg0.msgHdr : null; } } // Don't try to be smart with this because we need the loadStartFolder() // method to run even if startFolderURI is null otherwise our UI won't // properly restore. if (startMsgHdr) { await loadStartMsgHdr(startMsgHdr); } else { await loadStartFolder(startFolderURI); } } function HandleAppCommandEvent(evt) { evt.stopPropagation(); switch (evt.command) { case "Back": goDoCommand("cmd_goBack"); break; case "Forward": goDoCommand("cmd_goForward"); break; case "Stop": msgWindow.StopUrls(); break; case "Bookmarks": toAddressBook(); break; case "Home": case "Reload": default: break; } } /** * Called by the session store manager periodically and at shutdown to get * the state of this window for persistence. */ function getWindowStateForSessionPersistence() { let tabmail = document.getElementById("tabmail"); let tabsState = tabmail.persistTabs(); return { type: "3pane", tabs: tabsState }; } /** * Attempt to restore the previous tab states. * * @param {boolean} aDontRestoreFirstTab - If this is true, the first tab will * not be restored, and will continue to retain focus at the end. This is * needed if the window was opened with a folder or a message as an argument. * @returns true if the restoration was successful, false otherwise. */ async function atStartupRestoreTabs(aDontRestoreFirstTab) { let state = await SessionStoreManager.loadingWindow(window); if (state) { let tabsState = state.tabs; let tabmail = document.getElementById("tabmail"); try { tabmail.restoreTabs(tabsState, aDontRestoreFirstTab); } catch (e) { console.error(e); } } // It's now safe to load extra Tabs. loadExtraTabs(); // Note: The tabs have not finished loading at this point. SessionStoreManager._restored = true; Services.obs.notifyObservers(window, "mail-tabs-session-restored"); return !!state; } /** * Loads and restores tabs upon opening a window by evaluating window.arguments[1]. * * The type of the object is specified by it's action property. It can be * either "restore" or "open". "restore" invokes tabmail.restoreTab() for each * item in the tabs array. While "open" invokes tabmail.openTab() for each item. * * In case a tab can't be restored it will fail silently * * the object need at least the following properties: * * { * action = "restore" | "open" * tabs = []; * } * */ function loadExtraTabs() { if (!("arguments" in window) || window.arguments.length < 2) { return; } let tab = window.arguments[1]; if (!tab || typeof tab != "object") { return; } if ("wrappedJSObject" in tab) { tab = tab.wrappedJSObject; } let tabmail = document.getElementById("tabmail"); // we got no action, so suppose its "legacy" code if (!("action" in tab)) { if ("tabType" in tab) { tabmail.openTab(tab.tabType, tab.tabParams); } return; } if (!("tabs" in tab)) { return; } // this is used if a tab is detached to a new window. if (tab.action == "restore") { for (let i = 0; i < tab.tabs.length; i++) { tabmail.restoreTab(tab.tabs[i]); } // we currently do not support opening in background or opening a // special position. So select the last tab opened. tabmail.switchToTab(tabmail.tabInfo[tabmail.tabInfo.length - 1]); return; } if (tab.action == "open") { for (let i = 0; i < tab.tabs.length; i++) { if ("tabType" in tab.tabs[i]) { tabmail.openTab(tab.tabs[i].tabType, tab.tabs[i].tabParams); } } } } /** * Loads the given message header at window open. Exactly one out of this and * |loadStartFolder| should be called. * * @param aStartMsgHdr The message header to load at window open */ async function loadStartMsgHdr(aStartMsgHdr) { // We'll just clobber the default tab await atStartupRestoreTabs(true); MsgDisplayMessageInFolderTab(aStartMsgHdr); } async function loadStartFolder(initialUri) { var defaultServer = null; var startFolder; var isLoginAtStartUpEnabled = false; // If a URI was explicitly specified, we'll just clobber the default tab let loadFolder = !(await atStartupRestoreTabs(!!initialUri)); if (initialUri) { loadFolder = true; } // First get default account try { if (initialUri) { startFolder = MailUtils.getOrCreateFolder(initialUri); } else { let defaultAccount = MailServices.accounts.defaultAccount; if (!defaultAccount) { return; } defaultServer = defaultAccount.incomingServer; var rootMsgFolder = defaultServer.rootMsgFolder; startFolder = rootMsgFolder; // Enable check new mail once by turning checkmail pref 'on' to bring // all users to one plane. This allows all users to go to Inbox. User can // always go to server settings panel and turn off "Check for new mail at startup" if (!Services.prefs.getBoolPref(kMailCheckOncePrefName)) { Services.prefs.setBoolPref(kMailCheckOncePrefName, true); defaultServer.loginAtStartUp = true; } // Get the user pref to see if the login at startup is enabled for default account isLoginAtStartUpEnabled = defaultServer.loginAtStartUp; // Get Inbox only if login at startup is enabled. if (isLoginAtStartUpEnabled) { // now find Inbox var inboxFolder = rootMsgFolder.getFolderWithFlags( Ci.nsMsgFolderFlags.Inbox ); if (!inboxFolder) { return; } startFolder = inboxFolder; } } // it is possible we were given an initial uri and we need to subscribe or try to add // the folder. i.e. the user just clicked on a news folder they aren't subscribed to from a browser // the news url comes in here. // Perform biff on the server to check for new mail, except for imap // or a pop3 account that is deferred or deferred to, // or the case where initialUri is non-null (non-startup) if ( !initialUri && isLoginAtStartUpEnabled && !defaultServer.isDeferredTo && defaultServer.rootFolder == defaultServer.rootMsgFolder ) { defaultServer.performBiff(msgWindow); } if (loadFolder) { let tab = document.getElementById("tabmail")?.tabInfo[0]; tab.chromeBrowser.addEventListener( "load", () => (tab.folder = startFolder), true ); } } catch (ex) { console.error(ex); } MsgGetMessagesForAllServers(defaultServer); if (MailOfflineMgr.isOnline()) { // Check if we shut down offline, and restarted online, in which case // we may have offline events to playback. Since this is not a pref // the user should set, it's not in mailnews.js, so we need a try catch. let playbackOfflineEvents = Services.prefs.getBoolPref( "mailnews.playback_offline", false ); if (playbackOfflineEvents) { Services.prefs.setBoolPref("mailnews.playback_offline", false); MailOfflineMgr.offlineManager.goOnline(false, true, msgWindow); } // If appropriate, send unsent messages. This may end up prompting the user, // so we need to get it out of the flow of the normal load sequence. setTimeout(function () { if (MailOfflineMgr.shouldSendUnsentMessages()) { SendUnsentMessages(); } }, 0); } } function OpenMessageInNewTab(msgHdr, tabParams = {}) { if (!msgHdr) { return; } if (tabParams.background === undefined) { tabParams.background = Services.prefs.getBoolPref( "mail.tabs.loadInBackground" ); if (tabParams.event?.shiftKey) { tabParams.background = !tabParams.background; } } let tabmail = document.getElementById("tabmail"); tabmail.openTab("mailMessageTab", { ...tabParams, messageURI: msgHdr.folder.getUriForMsg(msgHdr), }); } function GetSelectedMsgFolders() { let tabInfo = document.getElementById("tabmail").currentTabInfo; if (tabInfo.mode.name == "mail3PaneTab") { let folder = tabInfo.folder; if (folder) { return [folder]; } } return []; } function SelectFolder(folderUri) { // TODO: Replace this. } function ReloadMessage() {} // Some of the per account junk mail settings have been // converted to global prefs. Let's try to migrate some // of those settings from the default account. function MigrateJunkMailSettings() { var junkMailSettingsVersion = Services.prefs.getIntPref("mail.spam.version"); if (!junkMailSettingsVersion) { // Get the default account, check to see if we have values for our // globally migrated prefs. let defaultAccount = MailServices.accounts.defaultAccount; if (defaultAccount) { // we only care about var prefix = "mail.server." + defaultAccount.incomingServer.key + "."; if (Services.prefs.prefHasUserValue(prefix + "manualMark")) { Services.prefs.setBoolPref( "mail.spam.manualMark", Services.prefs.getBoolPref(prefix + "manualMark") ); } if (Services.prefs.prefHasUserValue(prefix + "manualMarkMode")) { Services.prefs.setIntPref( "mail.spam.manualMarkMode", Services.prefs.getIntPref(prefix + "manualMarkMode") ); } if (Services.prefs.prefHasUserValue(prefix + "spamLoggingEnabled")) { Services.prefs.setBoolPref( "mail.spam.logging.enabled", Services.prefs.getBoolPref(prefix + "spamLoggingEnabled") ); } if (Services.prefs.prefHasUserValue(prefix + "markAsReadOnSpam")) { Services.prefs.setBoolPref( "mail.spam.markAsReadOnSpam", Services.prefs.getBoolPref(prefix + "markAsReadOnSpam") ); } } // bump the version so we don't bother doing this again. Services.prefs.setIntPref("mail.spam.version", 1); } } // The first time a user runs a build that supports folder views, pre-populate the favorite folders list // with the existing INBOX folders. function MigrateFolderViews() { var folderViewsVersion = Services.prefs.getIntPref( "mail.folder.views.version" ); if (!folderViewsVersion) { for (let server of MailServices.accounts.allServers) { if (server) { let inbox = MailUtils.getInboxFolder(server); if (inbox) { inbox.setFlag(Ci.nsMsgFolderFlags.Favorite); } } } Services.prefs.setIntPref("mail.folder.views.version", 1); } } // Do a one-time migration of the old mailnews.reuse_message_window pref to the // newer mail.openMessageBehavior. This does the migration only if the old pref // is defined. function MigrateOpenMessageBehavior() { let openMessageBehaviorVersion = Services.prefs.getIntPref( "mail.openMessageBehavior.version" ); if (!openMessageBehaviorVersion) { // Don't touch this if it isn't defined if ( Services.prefs.getPrefType("mailnews.reuse_message_window") == Ci.nsIPrefBranch.PREF_BOOL ) { if (Services.prefs.getBoolPref("mailnews.reuse_message_window")) { Services.prefs.setIntPref( "mail.openMessageBehavior", MailConsts.OpenMessageBehavior.EXISTING_WINDOW ); } else { Services.prefs.setIntPref( "mail.openMessageBehavior", MailConsts.OpenMessageBehavior.NEW_TAB ); } } Services.prefs.setIntPref("mail.openMessageBehavior.version", 1); } } function messageFlavorDataProvider() {} messageFlavorDataProvider.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIFlavorDataProvider"]), getFlavorData(aTransferable, aFlavor, aData) { if (aFlavor !== "application/x-moz-file-promise") { return; } let fileUriPrimitive = {}; aTransferable.getTransferData( "application/x-moz-file-promise-url", fileUriPrimitive ); let fileUriStr = fileUriPrimitive.value.QueryInterface( Ci.nsISupportsString ); let fileUri = Services.io.newURI(fileUriStr.data); let fileUrl = fileUri.QueryInterface(Ci.nsIURL); let fileName = fileUrl.fileName.replace(/(.{74}).*(.{10})$/u, "$1...$2"); let destDirPrimitive = {}; aTransferable.getTransferData( "application/x-moz-file-promise-dir", destDirPrimitive ); let destDirectory = destDirPrimitive.value.QueryInterface(Ci.nsIFile); let file = destDirectory.clone(); file.append(fileName); let messageUriPrimitive = {}; aTransferable.getTransferData("text/x-moz-message", messageUriPrimitive); let messageUri = messageUriPrimitive.value.QueryInterface( Ci.nsISupportsString ); let messenger = Cc["@mozilla.org/messenger;1"].createInstance( Ci.nsIMessenger ); messenger.saveAs( messageUri.data, true, null, decodeURIComponent(file.path), true ); }, }; var TabsInTitlebar = { init() { this._readPref(); Services.prefs.addObserver(this._drawInTitlePref, this); window.addEventListener("resolutionchange", this); window.addEventListener("resize", this); this._initialized = true; this.update(); }, allowedBy(condition, allow) { if (allow) { if (condition in this._disallowed) { delete this._disallowed[condition]; this.update(); } } else if (!(condition in this._disallowed)) { this._disallowed[condition] = null; this.update(); } }, get systemSupported() { let isSupported = false; switch (AppConstants.MOZ_WIDGET_TOOLKIT) { case "windows": case "cocoa": isSupported = true; break; case "gtk": isSupported = window.matchMedia("(-moz-gtk-csd-available)"); break; } delete this.systemSupported; return (this.systemSupported = isSupported); }, get enabled() { return document.documentElement.getAttribute("tabsintitlebar") == "true"; }, observe(subject, topic, data) { if (topic == "nsPref:changed") { this._readPref(); } }, handleEvent(aEvent) { switch (aEvent.type) { case "resolutionchange": if (aEvent.target == window) { this.update(); } break; case "resize": // The spaces toolbar needs special styling for the fullscreen mode. gSpacesToolbar.onWindowResize(); if (window.fullScreen || aEvent.target != window) { break; } // We use resize events because the window is not ready after // sizemodechange events. However, we only care about the event when // the sizemode is different from the last time we updated the // appearance of the tabs in the titlebar. let sizemode = document.documentElement.getAttribute("sizemode"); if (this._lastSizeMode == sizemode) { break; } let oldSizeMode = this._lastSizeMode; this._lastSizeMode = sizemode; // Don't update right now if we are leaving fullscreen, since the UI is // still changing in the consequent "fullscreen" event. Code there will // call this function again when everything is ready. // See browser-fullScreen.js: FullScreen.toggle and bug 1173768. if (oldSizeMode == "fullscreen") { break; } this.update(); break; } }, _initialized: false, _disallowed: {}, _drawInTitlePref: "mail.tabs.drawInTitlebar", _lastSizeMode: null, _readPref() { // check is only true when drawInTitlebar=true let check = Services.prefs.getBoolPref(this._drawInTitlePref); this.allowedBy("pref", check); }, update() { if (!this._initialized || window.fullScreen) { return; } let allowed = this.systemSupported && Object.keys(this._disallowed).length == 0; if ( document.documentElement.getAttribute("chromehidden")?.includes("toolbar") ) { // Don't draw in titlebar in case of a popup window. allowed = false; } if (allowed) { document.documentElement.setAttribute("tabsintitlebar", "true"); if (AppConstants.platform == "macosx") { document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1"); document.documentElement.removeAttribute("drawtitle"); } else { document.documentElement.setAttribute("chromemargin", "0,2,2,2"); } } else { document.documentElement.removeAttribute("tabsintitlebar"); document.documentElement.removeAttribute("chromemargin"); if (AppConstants.platform == "macosx") { document.documentElement.setAttribute("drawtitle", "true"); } } }, uninit() { this._initialized = false; Services.prefs.removeObserver(this._drawInTitlePref, this); }, }; var BrowserAddonUI = { async promptRemoveExtension(addon) { let { name } = addon; let [title, btnTitle] = await document.l10n.formatValues([ { id: "addon-removal-title", args: { name }, }, { id: "addon-removal-confirmation-button", }, ]); let { BUTTON_TITLE_IS_STRING: titleString, BUTTON_TITLE_CANCEL: titleCancel, BUTTON_POS_0, BUTTON_POS_1, confirmEx, } = Services.prompt; let btnFlags = BUTTON_POS_0 * titleString + BUTTON_POS_1 * titleCancel; let message = null; if (!Services.prefs.getBoolPref("prompts.windowPromptSubDialog", false)) { message = await document.l10n.formatValue( "addon-removal-confirmation-message", { name, } ); } let checkboxState = { value: false }; let result = confirmEx( window, title, message, btnFlags, btnTitle, /* button1 */ null, /* button2 */ null, /* checkboxMessage */ null, checkboxState ); return { remove: result === 0, report: false }; }, async removeAddon(addonId) { let addon = addonId && (await AddonManager.getAddonByID(addonId)); if (!addon || !(addon.permissions & AddonManager.PERM_CAN_UNINSTALL)) { return; } let { remove, report } = await this.promptRemoveExtension(addon); if (remove) { await addon.uninstall(report); } }, };