Bug 736055 - Fix Filelink URL insertion behaviour for composing, replying and forwarding. r+a=bienvenu.
authorMike Conley <mconley@mozilla.com>
Wed, 21 Mar 2012 16:59:13 -0400
changeset 11125 cbb93c0755c66e4aece3925c0bc4674af96acf24
parent 11124 c87d0eee72a8debe0c306872a4ca41574fb12e6c
child 11126 e7c441b317249980e2674a40056c2a0ae27f93e2
push id463
push userbugzilla@standard8.plus.com
push dateTue, 24 Apr 2012 17:34:51 +0000
treeherdercomm-beta@e53588e8f7b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs736055
Bug 736055 - Fix Filelink URL insertion behaviour for composing, replying and forwarding. r+a=bienvenu.
mail/components/compose/content/cloudAttachmentLinkManager.js
mailnews/compose/src/nsMsgCompose.cpp
--- a/mail/components/compose/content/cloudAttachmentLinkManager.js
+++ b/mail/components/compose/content/cloudAttachmentLinkManager.js
@@ -30,37 +30,39 @@ var gCloudAttachmentLinkManager = {
     bucket.removeEventListener("attachments-converted", this, false);
 
     gMsgCompose.UnregisterStateListener(this);
   },
 
   NotifyComposeFieldsReady: function() {},
 
   NotifyComposeBodyReady: function() {
-    // If we're doing an inline-forward in plain-text mode, let's
-    // take all of the current message text, and wrap it up into
-    // its own DIV.
-    if (gComposeType != Components.interfaces.nsIMsgCompType.ForwardInline ||
-        gMsgCompose.composeHTML)
+    // If we're doing an inline-forward, let's take all of the current
+    // message text, and wrap it up into its own DIV.
+    if (gComposeType != Components.interfaces.nsIMsgCompType.ForwardInline)
       return;
 
     let mailDoc = document.getElementById("content-frame").contentDocument;
     let mailBody = mailDoc.getElementsByTagName("body")[0];
     let editor = GetCurrentEditor();
+    let selection = editor.selection;
 
     let childNodes = mailBody.childNodes;
     let container = editor.createElementWithDefaults("div");
+    container.setAttribute("class", "moz-forward-container");
 
     if (mailBody.hasChildNodes()) {
       while (mailBody.childNodes.length > 0) {
         let removedChild = mailBody.removeChild(mailBody.firstChild);
         container.appendChild(removedChild);
       }
     }
+    editor.beginningOfDocument();
     editor.insertLineBreak();
+    selection.collapse(mailBody, 1);
     editor.insertElementAtSelection(container, false);
     editor.insertLineBreak();
   },
   ComposeProcessDone: function() {},
   SaveInFolderDone: function() {},
 
   handleEvent: function(event) {
     let mailDoc = document.getElementById("content-frame").contentDocument;
@@ -141,51 +143,139 @@ var gCloudAttachmentLinkManager = {
     const LINK_COLOR = "#0F7EDB";
     let link = aDocument.createElement("a");
     link.href = aHref;
     link.textContent = aContent;
     link.style.cssText = "color: " + LINK_COLOR + " !important";
     return link;
   },
 
+  _findInsertionPoint: function(aDocument) {
+    let mailBody = aDocument.getElementsByTagName("body")[0];
+    let editor = GetCurrentEditor();
+    let selection = editor.selection;
+
+    let childNodes = mailBody.childNodes;
+    let childToInsertAfter, childIndex;
+
+    // First, search for any text nodes that are immediate children of
+    // the body.  If we find any, we'll insert after those.
+    for (childIndex = childNodes.length - 1; childIndex >= 0; childIndex--) {
+      if (childNodes[childIndex].nodeType == Node.TEXT_NODE) {
+        childToInsertAfter = childNodes[childIndex];
+        break;
+      }
+    }
+
+    if (childIndex != -1) {
+      selection.collapse(childToInsertAfter,
+                         childToInsertAfter.nodeValue ?
+                         childToInsertAfter.nodeValue.length : 0);
+      if (childToInsertAfter.nodeValue &&
+          childToInsertAfter.nodeValue.length > 0)
+        editor.insertLineBreak();
+      editor.insertLineBreak();
+      return;
+    }
+
+    // If there's a signature, let's get a hold of it now.
+    let signature = mailBody.querySelector(".moz-signature");
+
+    // Are we replying?
+    let replyCitation = mailBody.querySelector(".moz-cite-prefix");
+    if (replyCitation) {
+      if (gCurrentIdentity && gCurrentIdentity.replyOnTop == 0) {
+        // Replying below quote - we'll select the point right before
+        // the signature.  If there's no signature, we'll just use the
+        // last node.
+        if (signature && signature.previousSibling)
+          selection.collapse(mailBody,
+                             Array.indexOf(childNodes,
+                                           signature.previousSibling));
+        else {
+          selection.collapse(mailBody, childNodes.length - 1);
+        }
+      } else {
+        // Replying above quote
+        if (replyCitation.previousSibling) {
+          let nodeIndex = Array.indexOf(childNodes, replyCitation.previousSibling);
+          if (nodeIndex <= 0) {
+            editor.insertLineBreak();
+            nodeIndex = 1;
+          }
+          selection.collapse(mailBody, nodeIndex);
+        } else {
+          editor.beginningOfDocument();
+          editor.insertLineBreak()
+        }
+      }
+      return;
+    }
+
+    // Are we forwarding?
+    let forwardBody = mailBody.querySelector(".moz-forward-container");
+    if (forwardBody) {
+      if (forwardBody.previousSibling) {
+        let nodeIndex = Array.indexOf(childNodes,
+                                      forwardBody.previousSibling);
+        if (nodeIndex <= 0) {
+          editor.insertLineBreak();
+          nodeIndex = 1;
+        }
+        // If we're forwarding, insert just before the forward body.
+        selection.collapse(mailBody, nodeIndex);
+      } else {
+        // Just insert after a linebreak at the top.
+        editor.beginningOfDocument();
+        editor.insertLineBreak();
+        selection.collapse(mailBody, 1);
+      }
+      return;
+    }
+
+    // If we haven't figured it out at this point, let's see if there's a
+    // signature, and just insert before it.
+    if (signature && signature.previousSibling) {
+      let nodeIndex = Array.indexOf(childNodes, signature.previousSibling);
+      if (nodeIndex <= 0) {
+        editor.insertLineBreak();
+        nodeIndex = 1;
+      }
+      selection.collapse(mailBody, nodeIndex);
+      return;
+    }
+
+    // If we haven't figured it out at this point, let's just put it
+    // at the bottom of the message body.  If the "bottom" is also the top,
+    // then we'll insert a linebreak just above it.
+    let nodeIndex = childNodes.length - 1;
+    if (nodeIndex <= 0) {
+      editor.insertLineBreak();
+      nodeIndex = 1;
+    }
+    selection.collapse(mailBody, nodeIndex);
+  },
+
   /**
    * Insert the header for the cloud attachment list, which we'll use to
    * as an insertion point for the individual cloud attachments.
    *
    * @param aDocument the document to insert the header into
    */
   _insertHeader: function(aDocument) {
     let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
-    let mailBody = aDocument.getElementsByTagName("body")[0];
     let editor = GetCurrentEditor();
     let selection = editor.selection;
 
     // Save off the selection ranges so we can restore them later.
     let ranges = [];
     for (let i = 0; i < selection.rangeCount; i++)
       ranges.push(selection.getRangeAt(i));
 
-    // Find the last text node that's a first level child of the body.
-    // This will skip quoted text.
-    let childNodes = mailBody.childNodes;
-    // If we don't find any text nodes, we'll insert at the start of the
-    // body, which may not be quite right.
-    let childToInsertAfter = childNodes[0];
-    for (let i = childNodes.length - 1; i >= 0; i--) {
-      if (childNodes[i].nodeType == Node.TEXT_NODE &&
-          childNodes[i].nodeValue && childNodes[i].nodeValue.length > 0) {
-        childToInsertAfter = childNodes[i];
-        break;
-      }
-    }
-
-    selection.collapse(childToInsertAfter,
-                       childToInsertAfter.nodeValue ?
-                       childToInsertAfter.nodeValue.length : 0);
-
+    this._findInsertionPoint(aDocument);
 
     if (gMsgCompose.composeHTML) {
       // It's really quite strange, but if we don't set
       // the innerHTML of each element to be non-empty, then
       // the nodes fail to be added to the compose window.
       let root = editor.createElementWithDefaults("div");
       root.id = "cloudAttachmentListRoot";
       root.style.padding = "15px";
@@ -217,38 +307,37 @@ var gCloudAttachmentLinkManager = {
       let footerMessage = getComposeBundle().getFormattedString("cloudAttachmentListFooter", [applink], 1);
 
       footer.innerHTML = footerMessage;
       footer.style.color = "#444444";
       footer.style.fontSize = "small";
       footer.style.marginTop = "15px";
       root.appendChild(footer);
 
-      editor.insertLineBreak();
       editor.insertElementAtSelection(root, false);
-      editor.insertLineBreak();
     }
     else {
+      let root = editor.createElementWithDefaults("div");
+      root.id = "cloudAttachmentListRoot";
+
       let header = editor.createElementWithDefaults("div");
       header.id = "cloudAttachmentListHeader";
       header.innerHTML = " ";
+      root.appendChild(header);
+
       let list = editor.createElementWithDefaults("span");
       list.id = "cloudAttachmentList";
-      list.innerHTML = " ";
+      root.appendChild(list);
 
-      editor.insertLineBreak();
-      editor.insertElementAtSelection(header, false);
-      editor.insertElementAtSelection(list, false);
-      editor.insertLineBreak();
+      editor.insertElementAtSelection(root, false);
     }
 
     // Restore the selection ranges.
     for (let [,range] in Iterator(ranges))
       selection.addRange(range);
-
   },
 
   /**
    * Updates the count of how many attachments have been added
    * in HTML emails.
    *
    * @aDocument the document that contains the cloudAttachmentListHeader node.
    */
--- a/mailnews/compose/src/nsMsgCompose.cpp
+++ b/mailnews/compose/src/nsMsgCompose.cpp
@@ -785,20 +785,21 @@ nsMsgCompose::ConvertAndLoadComposeWindo
       {
         if (mailEditor)
           mailEditor->InsertTextWithQuotations(aBuf);
         else
           textEditor->InsertText(aBuf);
         m_editor->EndOfDocument();
       }
 
-      if (!sigOnTop && !aSignature.IsEmpty())
+      if (!sigOnTop && !aSignature.IsEmpty()) {
         textEditor->InsertLineBreak();
         InsertDivWrappedTextAtSelection(aSignature,
                                         NS_LITERAL_STRING("moz-signature"));
+      }
     }
   }
 
   if (aBuf.IsEmpty())
     m_editor->BeginningOfDocument();
   else
   {
     switch (reply_on_top)