summaryrefslogtreecommitdiffstats
path: root/comm/mail/base/content/mailTabs.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/base/content/mailTabs.js')
-rw-r--r--comm/mail/base/content/mailTabs.js390
1 files changed, 390 insertions, 0 deletions
diff --git a/comm/mail/base/content/mailTabs.js b/comm/mail/base/content/mailTabs.js
new file mode 100644
index 0000000000..e805eb8afb
--- /dev/null
+++ b/comm/mail/base/content/mailTabs.js
@@ -0,0 +1,390 @@
+/* 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 mail3PaneWindowCommands.js */
+/* import-globals-from mailWindowOverlay.js */
+/* import-globals-from messenger.js */
+
+/* globals contentProgress, statusFeedback */ // From mailWindow.js
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ FolderUtils: "resource:///modules/FolderUtils.jsm",
+ GlodaSyntheticView: "resource:///modules/gloda/GlodaSyntheticView.jsm",
+ MailUtils: "resource:///modules/MailUtils.jsm",
+ MsgHdrSyntheticView: "resource:///modules/MsgHdrSyntheticView.jsm",
+ MsgHdrToMimeMessage: "resource:///modules/gloda/MimeMessage.jsm",
+});
+
+/**
+ * Tabs for displaying mail folders and messages.
+ */
+var mailTabType = {
+ name: "mailTab",
+ perTabPanel: "vbox",
+ _cloneTemplate(template, tab, onDOMContentLoaded, onLoad) {
+ let tabmail = document.getElementById("tabmail");
+
+ let clone = document.getElementById(template).content.cloneNode(true);
+ let browser = clone.querySelector("browser");
+ browser.id = `${tab.mode.name}Browser${tab.mode._nextId}`;
+ browser.addEventListener(
+ "DOMTitleChanged",
+ () => {
+ tab.title = browser.contentTitle;
+ tabmail.setTabTitle(tab);
+ },
+ true
+ );
+ let linkRelIconHandler = event => {
+ if (event.target.rel != "icon") {
+ return;
+ }
+ // Allow 3pane and message tab to set a tab favicon. Mail content should
+ // not be allowed to do that.
+ if (event.target.ownerGlobal.frameElement == browser) {
+ tabmail.setTabFavIcon(tab, event.target.href);
+ }
+ };
+ browser.addEventListener("DOMLinkAdded", linkRelIconHandler);
+ browser.addEventListener("DOMLinkChanged", linkRelIconHandler);
+ if (onDOMContentLoaded) {
+ browser.addEventListener(
+ "DOMContentLoaded",
+ event => {
+ if (!tab.closed) {
+ onDOMContentLoaded(event.target.ownerGlobal);
+ }
+ },
+ { capture: true, once: true }
+ );
+ }
+ browser.addEventListener(
+ "load",
+ event => {
+ if (!tab.closed) {
+ onLoad(event.target.ownerGlobal);
+ }
+ },
+ { capture: true, once: true }
+ );
+
+ tab.title = "";
+ tab.panel.id = `${tab.mode.name}${tab.mode._nextId}`;
+ tab.panel.appendChild(clone);
+ // `chromeBrowser` refers to the outermost browser in the tab, i.e. the
+ // browser displaying about:3pane or about:message.
+ tab.chromeBrowser = browser;
+ tab.mode._nextId++;
+ },
+
+ closeTab(tab) {},
+ saveTabState(tab) {},
+
+ modes: {
+ mail3PaneTab: {
+ _nextId: 1,
+ isDefault: true,
+
+ openTab(tab, args = {}) {
+ mailTabType._cloneTemplate(
+ "mail3PaneTabTemplate",
+ tab,
+ win => {
+ // Send the state to the page so it can restore immediately.
+ win.openingState = args;
+ },
+ async win => {
+ win.tabOrWindow = tab;
+ // onLoad has happened. async activities of scripts running of
+ // that may not have finished. Let's go back to the end of the
+ // event queue giving win.messageBrowser time to get defined.
+ await new Promise(resolve => win.setTimeout(resolve));
+ win.messageBrowser.contentWindow.tabOrWindow = tab;
+ if (!args.background) {
+ // Update telemetry once the tab has loaded and decided if the
+ // panes are visible.
+ Services.telemetry.keyedScalarSet(
+ "tb.ui.configuration.pane_visibility",
+ "folderPane",
+ win.paneLayout.folderPaneVisible
+ );
+ Services.telemetry.keyedScalarSet(
+ "tb.ui.configuration.pane_visibility",
+ "messagePane",
+ win.paneLayout.messagePaneVisible
+ );
+ }
+
+ // The first tab has loaded and ready for the user to interact with
+ // it. We can let the rest of the start-up happen now without
+ // appearing to slow the program down.
+ if (tab.first) {
+ Services.obs.notifyObservers(window, "mail-startup-done");
+ requestIdleCallback(function () {
+ if (!window.closed) {
+ Services.obs.notifyObservers(
+ window,
+ "mail-idle-startup-tasks-finished"
+ );
+ }
+ });
+ }
+ }
+ );
+
+ // `browser` and `linkedBrowser` refer to the message display browser
+ // within this tab. They may be null if the browser isn't visible.
+ // Extension APIs refer to these properties.
+ Object.defineProperty(tab, "browser", {
+ get() {
+ if (!tab.chromeBrowser.contentWindow) {
+ return null;
+ }
+
+ const { messageBrowser, webBrowser } =
+ tab.chromeBrowser.contentWindow;
+ if (messageBrowser && !messageBrowser.hidden) {
+ return messageBrowser.contentDocument.getElementById(
+ "messagepane"
+ );
+ }
+ if (webBrowser && !webBrowser.hidden) {
+ return webBrowser;
+ }
+
+ return null;
+ },
+ });
+ Object.defineProperty(tab, "linkedBrowser", {
+ get() {
+ return tab.browser;
+ },
+ });
+
+ // Content properties.
+ Object.defineProperty(tab, "message", {
+ get() {
+ let dbView = tab.chromeBrowser.contentWindow.gDBView;
+ if (dbView?.selection?.count) {
+ return dbView.hdrForFirstSelectedMessage;
+ }
+ return null;
+ },
+ });
+ Object.defineProperty(tab, "folder", {
+ get() {
+ return tab.chromeBrowser.contentWindow.gFolder;
+ },
+ set(folder) {
+ tab.chromeBrowser.contentWindow.displayFolder(folder.URI);
+ },
+ });
+
+ tab.canClose = !tab.first;
+ return tab;
+ },
+ persistTab(tab) {
+ if (!tab.folder) {
+ return null;
+ }
+ return {
+ firstTab: tab.first,
+ folderPaneVisible:
+ tab.chromeBrowser.contentWindow.paneLayout.folderPaneVisible,
+ folderURI: tab.folder.URI,
+ messagePaneVisible:
+ tab.chromeBrowser.contentWindow.paneLayout.messagePaneVisible,
+ };
+ },
+ restoreTab(tabmail, persistedState) {
+ if (!persistedState.firstTab) {
+ tabmail.openTab("mail3PaneTab", persistedState);
+ return;
+ }
+
+ // Manually call onTabRestored, since it is usually called by openTab(),
+ // which is skipped for the first tab.
+ let restoreState = tabmail._restoringTabState;
+ if (restoreState) {
+ for (let tabMonitor of tabmail.tabMonitors) {
+ try {
+ if (
+ "onTabRestored" in tabMonitor &&
+ restoreState &&
+ tabMonitor.monitorName in restoreState.ext
+ ) {
+ tabMonitor.onTabRestored(
+ tabmail.tabInfo[0],
+ restoreState.ext[tabMonitor.monitorName],
+ false
+ );
+ }
+ } catch (ex) {
+ console.error(ex);
+ }
+ }
+ }
+
+ let { chromeBrowser, closed } = tabmail.tabInfo[0];
+ if (
+ chromeBrowser.contentDocument.readyState == "complete" &&
+ chromeBrowser.currentURI.spec == "about:3pane"
+ ) {
+ chromeBrowser.contentWindow.restoreState(persistedState);
+ return;
+ }
+
+ // Send the state to the page so it can restore immediately. Don't
+ // overwrite any existing state properties from `openTab` (especially
+ // `first`), unless there is a newer value.
+ let sawDOMContentLoaded = false;
+ chromeBrowser.addEventListener(
+ "DOMContentLoaded",
+ event => {
+ if (!closed && event.target == chromeBrowser.contentDocument) {
+ let about3Pane = event.target.ownerGlobal;
+ about3Pane.openingState = {
+ ...about3Pane.openingState,
+ ...persistedState,
+ };
+ sawDOMContentLoaded = true;
+ }
+ },
+ { capture: true, once: true }
+ );
+ // Didn't see DOMContentLoaded? Restore the state on load. The state
+ // from `openTab` has been used by now.
+ chromeBrowser.addEventListener(
+ "load",
+ event => {
+ if (
+ !closed &&
+ !sawDOMContentLoaded &&
+ event.target == chromeBrowser.contentDocument
+ ) {
+ chromeBrowser.contentWindow.restoreState(persistedState);
+ }
+ },
+ { capture: true, once: true }
+ );
+ },
+ showTab(tab) {
+ if (
+ tab.chromeBrowser.currentURI.spec != "about:3pane" ||
+ tab.chromeBrowser.contentDocument.readyState != "complete"
+ ) {
+ return;
+ }
+
+ // Update telemetry when switching to a 3-pane tab. The telemetry
+ // reflects the state of the last 3-pane tab that was shown, but not
+ // if the state changed since it was shown.
+ Services.telemetry.keyedScalarSet(
+ "tb.ui.configuration.pane_visibility",
+ "folderPane",
+ tab.chromeBrowser.contentWindow.paneLayout.folderPaneVisible
+ );
+ Services.telemetry.keyedScalarSet(
+ "tb.ui.configuration.pane_visibility",
+ "messagePane",
+ tab.chromeBrowser.contentWindow.paneLayout.messagePaneVisible
+ );
+ },
+ supportsCommand(command, tab) {
+ return tab.chromeBrowser?.contentWindow.commandController?.supportsCommand(
+ command
+ );
+ },
+ isCommandEnabled(command, tab) {
+ return tab.chromeBrowser?.contentWindow.commandController?.isCommandEnabled(
+ command
+ );
+ },
+ doCommand(command, tab, ...args) {
+ tab.chromeBrowser?.contentWindow.commandController?.doCommand(
+ command,
+ ...args
+ );
+ },
+ getBrowser(tab) {
+ return tab.browser;
+ },
+ },
+ mailMessageTab: {
+ _nextId: 1,
+ openTab(tab, { messageURI, viewWrapper } = {}) {
+ mailTabType._cloneTemplate(
+ "mailMessageTabTemplate",
+ tab,
+ win => {
+ // Make tabmail give the message pane focus when this tab becomes
+ // the active tab.
+ tab.lastActiveElement = tab.browser;
+ },
+ win => {
+ win.tabOrWindow = tab;
+ win.displayMessage(messageURI, viewWrapper);
+ }
+ );
+
+ // `browser` and `linkedBrowser` refer to the message display browser
+ // within this tab. They may be null if the browser isn't visible.
+ // Extension APIs refer to these properties.
+ Object.defineProperty(tab, "browser", {
+ get() {
+ return tab.chromeBrowser.contentDocument?.getElementById(
+ "messagepane"
+ );
+ },
+ });
+ Object.defineProperty(tab, "linkedBrowser", {
+ get() {
+ return tab.browser;
+ },
+ });
+
+ // Content properties.
+ Object.defineProperty(tab, "message", {
+ get() {
+ return tab.chromeBrowser.contentWindow.gMessage;
+ },
+ });
+ Object.defineProperty(tab, "folder", {
+ get() {
+ return tab.chromeBrowser.contentWindow.gViewWrapper
+ ?.displayedFolder;
+ },
+ });
+
+ return tab;
+ },
+ persistTab(tab) {
+ return { messageURI: tab.chromeBrowser.contentWindow.gMessageURI };
+ },
+ restoreTab(tabmail, persistedState) {
+ tabmail.openTab("mailMessageTab", persistedState);
+ },
+ showTab(tab) {},
+ supportsCommand(command, tab) {
+ return tab.chromeBrowser?.contentWindow.commandController?.supportsCommand(
+ command
+ );
+ },
+ isCommandEnabled(command, tab) {
+ return tab.chromeBrowser.contentWindow.commandController?.isCommandEnabled(
+ command
+ );
+ },
+ doCommand(command, tab, ...args) {
+ tab.chromeBrowser?.contentWindow.commandController?.doCommand(
+ command,
+ ...args
+ );
+ },
+ getBrowser(tab) {
+ return tab.browser;
+ },
+ },
+ },
+};