summaryrefslogtreecommitdiffstats
path: root/comm/suite/mailnews/content/mailWindow.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--comm/suite/mailnews/content/mailWindow.js593
1 files changed, 593 insertions, 0 deletions
diff --git a/comm/suite/mailnews/content/mailWindow.js b/comm/suite/mailnews/content/mailWindow.js
new file mode 100644
index 0000000000..388ebe2cb1
--- /dev/null
+++ b/comm/suite/mailnews/content/mailWindow.js
@@ -0,0 +1,593 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ //This file stores variables common to mail windows
+
+var messenger;
+var statusFeedback;
+var msgWindow;
+
+var msgComposeService;
+var accountManager;
+var RDF;
+var msgComposeType;
+var msgComposeFormat;
+
+var gMessengerBundle;
+var gBrandBundle;
+
+var accountCentralBox = null;
+var gDisableViewsSearch = null;
+var gAccountCentralLoaded = true;
+//End progress and Status variables
+
+var gOfflineManager;
+
+function OnMailWindowUnload()
+{
+ RemoveMailOfflineObserver();
+ ClearPendingReadTimer();
+
+ var searchSession = GetSearchSession();
+ if (searchSession)
+ {
+ removeGlobalListeners();
+ if (gPreQuickSearchView) //close the cached pre quick search view
+ gPreQuickSearchView.close();
+ }
+
+ var dbview = GetDBView();
+ if (dbview) {
+ dbview.close();
+ }
+
+ MailServices.mailSession.RemoveFolderListener(folderListener);
+
+ MailServices.mailSession.RemoveMsgWindow(msgWindow);
+ messenger.setWindow(null, null);
+
+ msgWindow.closeWindow();
+
+ msgWindow.msgHeaderSink = null;
+ msgWindow.notificationCallbacks = null;
+ gDBView = null;
+}
+
+/**
+ * When copying/dragging, convert imap/mailbox URLs of images into data URLs so
+ * that the images can be accessed in a paste elsewhere.
+ */
+function onCopyOrDragStart(e) {
+ let browser = getBrowser();
+ if (!browser) {
+ return;
+ }
+ let sourceDoc = browser.contentDocument;
+ if (e.target.ownerDocument != sourceDoc) {
+ // We're only interested if this is in the message content.
+ return;
+ }
+
+ let imgMap = new Map(); // Mapping img.src -> dataURL.
+
+ // For copy, the data of what is to be copied is not accessible at this point.
+ // Figure out what images are a) part of the selection and b) visible in
+ // the current document. If their source isn't http or data already, convert
+ // them to data URLs.
+ let selection = sourceDoc.getSelection();
+ let draggedImg = selection.isCollapsed ? e.target : null;
+ for (let img of sourceDoc.images) {
+ if (/^(https?|data):/.test(img.src)) {
+ continue;
+ }
+
+ if (img.naturalWidth == 0) {
+ // Broken/inaccessible image then...
+ continue;
+ }
+
+ if (!draggedImg && !selection.containsNode(img, true)) {
+ continue;
+ }
+
+ let style = window.getComputedStyle(img);
+ if (style.display == "none" || style.visibility == "hidden") {
+ continue;
+ }
+
+ // Do not convert if the image is specifically flagged to not snarf.
+ if (img.getAttribute("moz-do-not-send") == "true") {
+ continue;
+ }
+
+ // We don't need to wait for the image to load. If it isn't already loaded
+ // in the source document, we wouldn't want it anyway.
+ let canvas = sourceDoc.createElement("canvas");
+ canvas.width = img.width;
+ canvas.height = img.height;
+ canvas.getContext("2d").drawImage(img, 0, 0, img.width, img.height);
+
+ let type = /\.jpe?g$/i.test(img.src) ? "image/jpg" : "image/png";
+ imgMap.set(img.src, canvas.toDataURL(type));
+ }
+
+ if (imgMap.size == 0) {
+ // Nothing that needs converting!
+ return;
+ }
+
+ let clonedSelection = draggedImg ? draggedImg.cloneNode(false) :
+ selection.getRangeAt(0).cloneContents();
+ let div = sourceDoc.createElement("div");
+ div.appendChild(clonedSelection);
+
+ let images = div.querySelectorAll("img");
+ for (let img of images) {
+ if (!imgMap.has(img.src)) {
+ continue;
+ }
+ img.src = imgMap.get(img.src);
+ }
+
+ let html = div.innerHTML;
+ let parserUtils = Cc["@mozilla.org/parserutils;1"]
+ .getService(Ci.nsIParserUtils);
+ let plain =
+ parserUtils.convertToPlainText(html,
+ Ci.nsIDocumentEncoder.OutputForPlainTextClipboardCopy,
+ 0);
+
+ // Copy operation.
+ if ("clipboardData" in e) {
+ e.clipboardData.setData("text/html", html);
+ e.clipboardData.setData("text/plain", plain);
+ e.preventDefault();
+ }
+ // Drag operation.
+ else if ("dataTransfer" in e) {
+ e.dataTransfer.setData("text/html", html);
+ e.dataTransfer.setData("text/plain", plain);
+ }
+}
+
+function CreateMailWindowGlobals()
+{
+ // Get the messenger instance.
+ messenger = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger);
+
+ // Create windows status feedback
+ // set the JS implementation of status feedback before creating the c++ one..
+ window.MsgStatusFeedback = new nsMsgStatusFeedback();
+ // Double register the status feedback object as the xul browser window
+ // implementation.
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIXULWindow)
+ .XULBrowserWindow = window.MsgStatusFeedback;
+
+ statusFeedback = Cc["@mozilla.org/messenger/statusfeedback;1"]
+ .createInstance(Ci.nsIMsgStatusFeedback);
+ statusFeedback.setWrappedStatusFeedback(window.MsgStatusFeedback);
+
+ window.MsgWindowCommands = new nsMsgWindowCommands();
+
+ //Create message window object
+ msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"]
+ .createInstance(Ci.nsIMsgWindow);
+
+ msgComposeService = Cc['@mozilla.org/messengercompose;1']
+ .getService(Ci.nsIMsgComposeService);
+
+ accountManager = MailServices.accounts;
+
+ RDF = Cc['@mozilla.org/rdf/rdf-service;1']
+ .getService(Ci.nsIRDFService);
+
+ msgComposeType = Ci.nsIMsgCompType;
+ msgComposeFormat = Ci.nsIMsgCompFormat;
+
+ gMessengerBundle = document.getElementById("bundle_messenger");
+ gBrandBundle = document.getElementById("bundle_brand");
+
+ msgWindow.notificationCallbacks = new nsMsgBadCertHandler();
+}
+
+function InitMsgWindow()
+{
+ msgWindow.windowCommands = new nsMsgWindowCommands();
+ // set the domWindow before setting the status feedback and header sink objects
+ msgWindow.domWindow = window;
+ msgWindow.statusFeedback = statusFeedback;
+ msgWindow.msgHeaderSink = messageHeaderSink;
+ MailServices.mailSession.AddMsgWindow(msgWindow);
+
+ var messagepane = getMessageBrowser();
+ messagepane.docShell.allowAuth = false;
+ messagepane.docShell.allowDNSPrefetch = false;
+ msgWindow.rootDocShell.allowAuth = true;
+ msgWindow.rootDocShell.appType = Ci.nsIDocShell.APP_TYPE_MAIL;
+ // Ensure we don't load xul error pages into the main window
+ msgWindow.rootDocShell.useErrorPages = false;
+
+ document.addEventListener("copy", onCopyOrDragStart, true);
+ document.addEventListener("dragstart", onCopyOrDragStart, true);
+}
+
+function messagePaneOnResize(event)
+{
+ // scale any overflowing images
+ var messagepane = getMessageBrowser();
+ var doc = messagepane.contentDocument;
+ var imgs = doc.images;
+ for (var img of imgs)
+ {
+ if (img.className == "moz-attached-image")
+ {
+ if (img.naturalWidth <= doc.body.clientWidth)
+ {
+ img.removeAttribute("isshrunk");
+ img.removeAttribute("overflowing");
+ }
+ else if (img.hasAttribute("shrinktofit"))
+ {
+ img.setAttribute("isshrunk", "true");
+ img.removeAttribute("overflowing");
+ }
+ else
+ {
+ img.setAttribute("overflowing", "true");
+ img.removeAttribute("isshrunk");
+ }
+ }
+ }
+
+}
+
+function messagePaneOnClick(event)
+{
+ // if this is stand alone mail (no browser)
+ // or this isn't a simple left click, do nothing, and let the normal code execute
+ if (event.button != 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
+ return contentAreaClick(event);
+
+ // try to determine the href for what you are clicking on.
+ // for example, it might be "" if you aren't left clicking on a link
+ var ceParams = hrefAndLinkNodeForClickEvent(event);
+ if (!ceParams && !event.button)
+ {
+ var target = event.target;
+ // is this an image that we might want to scale?
+ if (target instanceof Ci.nsIImageLoadingContent)
+ {
+ // make sure it loaded successfully
+ var req = target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ if (!req || req.imageStatus & Ci.imgIRequest.STATUS_ERROR)
+ return true;
+ // is it an inline attachment?
+ if (/^moz-attached-image/.test(target.className))
+ {
+ if (target.hasAttribute("isshrunk"))
+ {
+ // currently shrunk to fit, so unshrink it
+ target.removeAttribute("isshrunk");
+ target.removeAttribute("shrinktofit");
+ target.setAttribute("overflowing", "true");
+ }
+ else if (target.hasAttribute("overflowing"))
+ {
+ // user wants to shrink now
+ target.setAttribute("isshrunk", "true");
+ target.setAttribute("shrinktofit", "true");
+ target.removeAttribute("overflowing");
+ }
+ }
+ }
+ return true;
+ }
+ var href = ceParams.href;
+
+ // we know that http://, https://, ftp://, file://, chrome://,
+ // resource://, and about, should load in a browser. but if
+ // we don't have one of those (examples are mailto, imap, news, mailbox, snews,
+ // nntp, ldap, and externally handled schemes like aim) we may or may not
+ // want a browser window, in which case we return here and let the normal code
+ // handle it
+ var needABrowser = /(^http(s)?:|^ftp:|^file:|^chrome:|^resource:|^about:)/i;
+ if (href.search(needABrowser) == -1)
+ return true;
+
+ // however, if the protocol should not be loaded internally, then we should
+ // not put up a new browser window. we should just let the usual processing
+ // take place.
+ try {
+ var extProtService = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService);
+ var scheme = href.substring(0, href.indexOf(":"));
+ if (!extProtService.isExposedProtocol(scheme))
+ return true;
+ }
+ catch (ex) {} // ignore errors, and just assume that we can proceed.
+
+ // if you get here, the user did a simple left click on a link
+ // that we know should be in a browser window.
+ // since we are in the message pane, send it to the top most browser window
+ // (or open one) right away, instead of waiting for us to get some data and
+ // determine the content type, and then open a browser window
+ // we want to preventDefault, so that in
+ // nsGenericHTMLElement::HandleDOMEventForAnchors(), we don't try to handle the click again
+ event.preventDefault();
+ if (isPhishingURL(ceParams.linkNode, false, href))
+ return false;
+
+ openAsExternal(href);
+ return true;
+}
+
+// We're going to implement our status feedback for the mail window in JS now.
+// the following contains the implementation of our status feedback object
+
+function nsMsgStatusFeedback()
+{
+}
+
+nsMsgStatusFeedback.prototype =
+{
+ // global variables for status / feedback information....
+ statusTextFld : null,
+ statusBar : null,
+ statusPanel : null,
+ throbber : null,
+ stopCmd : null,
+ startTimeoutID : null,
+ stopTimeoutID : null,
+ pendingStartRequests : 0,
+ meteorsSpinning : false,
+ myDefaultStatus : "",
+
+ ensureStatusFields : function()
+ {
+ if (!this.statusTextFld ) this.statusTextFld = document.getElementById("statusText");
+ if (!this.statusBar) this.statusBar = document.getElementById("statusbar-icon");
+ if (!this.statusPanel) this.statusPanel = document.getElementById("statusbar-progresspanel");
+ if (!this.throbber) this.throbber = document.getElementById("navigator-throbber");
+ if (!this.stopCmd) this.stopCmd = document.getElementById("cmd_stop");
+ },
+
+ // nsIXULBrowserWindow implementation
+ setJSStatus : function(status)
+ {
+ if (status.length > 0)
+ this.showStatusString(status);
+ },
+ setOverLink : function(link, context)
+ {
+ this.ensureStatusFields();
+ this.statusTextFld.label = link;
+ },
+
+ // Called before links are navigated to to allow us to retarget them if needed.
+ onBeforeLinkTraversal: function(aOriginalTarget, aLinkURI, aLinkNode, aIsAppTab)
+ {
+ return aOriginalTarget;
+ },
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIMsgStatusFeedback) ||
+ iid.equals(Ci.nsIXULBrowserWindow) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ // nsIMsgStatusFeedback implementation.
+ showStatusString : function(statusText)
+ {
+ this.ensureStatusFields();
+ if ( !statusText.length )
+ statusText = this.myDefaultStatus;
+ else
+ this.myDefaultStatus = "";
+ this.statusTextFld.label = statusText;
+ },
+ setStatusString : function(status)
+ {
+ if (status.length > 0)
+ {
+ this.myDefaultStatus = status;
+ this.statusTextFld.label = status;
+ }
+ },
+ _startMeteors : function()
+ {
+ this.ensureStatusFields();
+
+ this.meteorsSpinning = true;
+ this.startTimeoutID = null;
+
+ // Show progress meter
+ this.statusPanel.collapsed = false;
+
+ // Turn progress meter on.
+ this.statusBar.setAttribute("mode","undetermined");
+
+ // start the throbber
+ if (this.throbber)
+ this.throbber.setAttribute("busy", true);
+
+ //turn on stop button and menu
+ if (this.stopCmd)
+ this.stopCmd.removeAttribute("disabled");
+ },
+ startMeteors : function()
+ {
+ this.pendingStartRequests++;
+ // if we don't already have a start meteor timeout pending
+ // and the meteors aren't spinning, then kick off a start
+ if (!this.startTimeoutID && !this.meteorsSpinning && window.MsgStatusFeedback)
+ this.startTimeoutID = setTimeout('window.MsgStatusFeedback._startMeteors();', 500);
+
+ // since we are going to start up the throbber no sense in processing
+ // a stop timeout...
+ if (this.stopTimeoutID)
+ {
+ clearTimeout(this.stopTimeoutID);
+ this.stopTimeoutID = null;
+ }
+ },
+ _stopMeteors : function()
+ {
+ this.ensureStatusFields();
+ this.showStatusString(this.myDefaultStatus);
+
+ // stop the throbber
+ if (this.throbber)
+ this.throbber.setAttribute("busy", false);
+
+ // Turn progress meter off.
+ this.statusPanel.collapsed = true;
+ this.statusBar.setAttribute("mode","normal");
+ this.statusBar.value = 0; // be sure to clear the progress bar
+ this.statusBar.label = "";
+ if (this.stopCmd)
+ this.stopCmd.setAttribute("disabled", "true");
+
+ this.meteorsSpinning = false;
+ this.stopTimeoutID = null;
+ },
+ stopMeteors : function()
+ {
+ if (this.pendingStartRequests > 0)
+ this.pendingStartRequests--;
+
+ // if we are going to be starting the meteors, cancel the start
+ if (this.pendingStartRequests == 0 && this.startTimeoutID)
+ {
+ clearTimeout(this.startTimeoutID);
+ this.startTimeoutID = null;
+ }
+
+ // if we have no more pending starts and we don't have a stop timeout already in progress
+ // AND the meteors are currently running then fire a stop timeout to shut them down.
+ if (this.pendingStartRequests == 0 && !this.stopTimeoutID)
+ {
+ if (this.meteorsSpinning && window.MsgStatusFeedback)
+ this.stopTimeoutID = setTimeout('window.MsgStatusFeedback._stopMeteors();', 500);
+ }
+ },
+ showProgress : function(percentage)
+ {
+ this.ensureStatusFields();
+ if (percentage >= 0)
+ {
+ this.statusBar.setAttribute("mode", "normal");
+ this.statusBar.value = percentage;
+ this.statusBar.label = Math.round(percentage) + "%";
+ }
+ }
+}
+
+
+function nsMsgWindowCommands()
+{
+}
+
+nsMsgWindowCommands.prototype =
+{
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIMsgWindowCommands) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ selectFolder: function(folderUri)
+ {
+ gFolderTreeView.selectFolder(MailUtils.getFolderForURI(folderUri));
+ },
+
+ selectMessage: function(messageUri)
+ {
+ SelectMessage(messageUri);
+ },
+
+ clearMsgPane: function()
+ {
+ if (gDBView)
+ setTitleFromFolder(gDBView.msgFolder,null);
+ else
+ setTitleFromFolder(null,null);
+ ClearMessagePane();
+ }
+}
+
+function StopUrls()
+{
+ msgWindow.StopUrls();
+}
+
+function loadStartPage()
+{
+ try
+ {
+ gMessageNotificationBar.clearMsgNotifications();
+
+ var startpageenabled = Services.prefs.getBoolPref("mailnews.start_page.enabled");
+ if (startpageenabled)
+ {
+ var startpage = GetLocalizedStringPref("mailnews.start_page.url");
+ if (startpage)
+ {
+ GetMessagePaneFrame().location.href = startpage;
+ //dump("start message pane with: " + startpage + "\n");
+ ClearMessageSelection();
+ }
+ }
+ }
+ catch (ex)
+ {
+ dump("Error loading start page.\n");
+ return;
+ }
+}
+
+// Given the server, open the twisty and the set the selection
+// on inbox of that server.
+// prompt if offline.
+function OpenInboxForServer(server)
+{
+ ShowThreadPane();
+ gFolderTreeView.selectFolder(GetInboxFolder(server));
+
+ if (!Services.io.offline) {
+ if (server.type != "imap")
+ GetMessagesForInboxOnServer(server);
+ }
+ else if (DoGetNewMailWhenOffline()) {
+ GetMessagesForInboxOnServer(server);
+ }
+}
+
+function GetSearchSession()
+{
+ if (("gSearchSession" in top) && gSearchSession)
+ return gSearchSession;
+ else
+ return null;
+}
+
+function MailSetCharacterSet(aEvent)
+{
+ if (aEvent.target.hasAttribute("charset")) {
+ msgWindow.mailCharacterSet = aEvent.target.getAttribute("charset");
+ msgWindow.charsetOverride = true;
+ }
+ messenger.setDocumentCharset(msgWindow.mailCharacterSet);
+}