Bug 530629 - For composition, implement find bar (Ctrl+F) with "Replace..." button. r=Neil, r=mkmelin, ui-r=Paenglab, ui-r=JosiahOne
authorSuyash Agarwal <syshagarwal@gmail.com>
Fri, 18 Apr 2014 09:25:28 -0400 (2014-04-18)
changeset 16037 fd84c515f4c8f904db4bbb6dccfeb4a051bb16e9
parent 16036 494eb7379715f7ce1b81546eef4d4100de915ffe
child 16038 b1d860289be90a0e77d5179af07721c02c3ebc17
push id10041
push userryanvm@gmail.com
push dateFri, 18 Apr 2014 13:26:17 +0000 (2014-04-18)
treeherdercomm-central@da2a1fab3f66 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeil, mkmelin, Paenglab, JosiahOne
bugs530629
Bug 530629 - For composition, implement find bar (Ctrl+F) with "Replace..." button. r=Neil, r=mkmelin, ui-r=Paenglab, ui-r=JosiahOne
editor/ui/composer/content/ComposerCommands.js
editor/ui/composer/content/editingOverlay.js
editor/ui/composer/content/editor.xul
editor/ui/composer/content/editorOverlay.xul
editor/ui/locales/en-US/chrome/composer/editorOverlay.dtd
mail/components/compose/content/MsgComposeCommands.js
mail/components/compose/content/editorOverlay.xul
mail/components/compose/content/messengercompose.xul
mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
mail/locales/en-US/chrome/messenger/messengercompose/messengercompose.dtd
mail/themes/shared/mail/messenger.css
mail/themes/windows/mail/compose/messengercompose-aero.css
mail/themes/windows/mail/compose/messengercompose.css
suite/common/unix/platformCommunicatorOverlay.xul
suite/common/utilityOverlay.xul
suite/common/win/platformCommunicatorOverlay.xul
suite/locales/en-US/chrome/common/utilityOverlay.dtd
suite/mailnews/compose/MsgComposeCommands.js
suite/mailnews/compose/messengercompose.xul
--- a/editor/ui/composer/content/ComposerCommands.js
+++ b/editor/ui/composer/content/ComposerCommands.js
@@ -80,16 +80,17 @@ function SetupHTMLEditorCommands()
 function SetupTextEditorCommands()
 {
   var commandTable = GetComposerCommandTable();
   if (!commandTable)
     return;
 
   //dump("Registering plain text editor commands\n");
 
+  commandTable.registerCommand("cmd_findReplace",nsFindReplaceCommand);
   commandTable.registerCommand("cmd_find",       nsFindCommand);
   commandTable.registerCommand("cmd_findNext",   nsFindAgainCommand);
   commandTable.registerCommand("cmd_findPrev",   nsFindAgainCommand);
   commandTable.registerCommand("cmd_rewrap",     nsRewrapCommand);
   commandTable.registerCommand("cmd_spelling",   nsSpellingCommand);
   commandTable.registerCommand("cmd_validate",   nsValidateCommand);
   commandTable.registerCommand("cmd_checkLinks", nsCheckLinksCommand);
   commandTable.registerCommand("cmd_insertChars", nsInsertCharsCommand);
@@ -2238,35 +2239,47 @@ var nsPrintSetupCommand =
   {
     // In editor.js
     SetEditMode(gPreviousNonSourceDisplayMode);
     PrintUtils.showPageSetup();
   }
 };
 
 //-----------------------------------------------------------------------------------
+var nsFindReplaceCommand =
+{
+  isCommandEnabled: function(aCommand, editorElement)
+  {
+    return editorElement.getEditor(editorElement.contentWindow) != null;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, editorElement) {},
+  doCommandParams: function(aCommand, aParams, editorElement) {},
+
+  doCommand: function(aCommand, editorElement)
+  {
+    window.openDialog("chrome://editor/content/EdReplace.xul", "_blank",
+                      "chrome,modal,titlebar", editorElement);
+  }
+};
+
+//-----------------------------------------------------------------------------------
 var nsFindCommand =
 {
   isCommandEnabled: function(aCommand, editorElement)
   {
     return editorElement.getEditor(editorElement.contentWindow) != null;
   },
 
   getCommandStateParams: function(aCommand, aParams, editorElement) {},
   doCommandParams: function(aCommand, aParams, editorElement) {},
 
   doCommand: function(aCommand, editorElement)
   {
-    try {
-      window.openDialog("chrome://editor/content/EdReplace.xul", "_blank",
-                        "chrome,modal,titlebar", editorElement);
-    }
-    catch(ex) {
-      dump("*** Exception: couldn't open Replace Dialog\n");
-    }
+    document.getElementById("FindToolbar").onFindCommand();
   }
 };
 
 //-----------------------------------------------------------------------------------
 var nsFindAgainCommand =
 {
   isCommandEnabled: function(aCommand, editorElement)
   {
@@ -2275,27 +2288,18 @@ var nsFindAgainCommand =
     return editorElement.getEditor(editorElement.contentWindow) != null;
   },
 
   getCommandStateParams: function(aCommand, aParams, editorElement) {},
   doCommandParams: function(aCommand, aParams, editorElement) {},
 
   doCommand: function(aCommand, editorElement)
   {
-    try {
-      var findPrev = aCommand == "cmd_findPrev";
-      var findInst = editorElement.webBrowserFind;
-      var findService = Components.classes["@mozilla.org/find/find_service;1"]
-                                  .getService(Components.interfaces.nsIFindService);
-      findInst.findBackwards = findService.findBackwards ^ findPrev;
-      findInst.findNext();
-      // reset to what it was in dialog, otherwise dialog setting can get reversed
-      findInst.findBackwards = findService.findBackwards;
-    }
-    catch (ex) {}
+    let findPrev = (aCommand == "cmd_findPrev");
+    document.getElementById("FindToolbar").onFindAgainCommand(findPrev);
   }
 };
 
 //-----------------------------------------------------------------------------------
 var nsRewrapCommand =
 {
   isCommandEnabled: function(aCommand, dummy)
   {
--- a/editor/ui/composer/content/editingOverlay.js
+++ b/editor/ui/composer/content/editingOverlay.js
@@ -71,16 +71,17 @@ function EditorOnLoad()
     gSourceTextEditor.rootElement.style.margin = 0;
     var controller = Components.classes["@mozilla.org/embedcomp/base-command-controller;1"]
                                .createInstance(Components.interfaces.nsIControllerContext);
     controller.init(null);
     controller.setCommandContext(gSourceContentWindow);
     gSourceContentWindow.contentWindow.controllers.insertControllerAt(0, controller);
     var commandTable = controller.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                                  .getInterface(Components.interfaces.nsIControllerCommandTable);
+    commandTable.registerCommand("cmd_findReplace", nsFindReplaceCommand);
     commandTable.registerCommand("cmd_find",        nsFindCommand);
     commandTable.registerCommand("cmd_findNext",    nsFindAgainCommand);
     commandTable.registerCommand("cmd_findPrev",    nsFindAgainCommand);
   } catch (e) {
     dump("makeEditable failed: "+e+"\n");
   }
 }
 
--- a/editor/ui/composer/content/editor.xul
+++ b/editor/ui/composer/content/editor.xul
@@ -310,30 +310,27 @@
 <!-- sidebar/toolbar/content/status -->
 <hbox id="sidebar-parent" flex="1">
   <!-- From sidebarOverlay.xul -->
   <vbox id="sidebar-box" class="chromeclass-extrachrome" hidden="true"/>
   <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
 
   <vbox id="appcontent" flex="1">
     <deck id="ContentWindowDeck" selectedIndex="0" flex="1">
-      <!-- KLUDGE:  Temporary fix for bug 34414:
-           The current editor tag doesn't have a view, 
-           which breaks deck frame-hiding mechanism 
-       -->
-      <stack>
+      <vbox>
+        <findbar id="FindToolbar" browserid="content-frame"/>
         <editor editortype="html"
                 type="content-primary"
                 id="content-frame"
                 onclick="EditorClick(event);"
                 ondblclick="EditorDblClick(event);"
                 context="contentAreaContextMenu"
                 flex="1"
                 tooltip="aHTMLTooltip"/>
-      </stack>
+      </vbox>
       <vbox>
         <label id="doctype-text" crop="right"/>
         <editor type="content"
                 id="content-source"
                 context="contentAreaContextMenu"
                 flex="1"/>
       </vbox>
     </deck>
--- a/editor/ui/composer/content/editorOverlay.xul
+++ b/editor/ui/composer/content/editorOverlay.xul
@@ -169,16 +169,17 @@
     <command id="cmd_cut"/>
     <command id="cmd_copy"/>
     <command id="cmd_paste"/>  
     <command id="cmd_pasteNoFormatting" oncommand="goDoCommand('cmd_pasteNoFormatting')"
              label="&pasteNoFormatting.label;" accesskey="&pasteNoFormatting.accesskey;"/>
     <command id="cmd_delete"/>
     <command id="cmd_selectAll"/>
     <command id="cmd_preferences"   oncommand="goDoCommand('cmd_preferences')"/>
+    <command id="cmd_findReplace"   oncommand="goDoCommand('cmd_findReplace')"/>
     <command id="cmd_find"          oncommand="goDoCommand('cmd_find')"/>
     <command id="cmd_findNext"      oncommand="goDoCommand('cmd_findNext');"/>
     <command id="cmd_findPrev"      oncommand="goDoCommand('cmd_findPrev');"/>
     <command id="cmd_spelling"      oncommand="goDoCommand('cmd_spelling')"/>
     <command id="cmd_checkLinks"    oncommand="goDoCommand('cmd_checkLinks')"/>
     <command id="cmd_pasteQuote"
              label="&pasteAsQuotationCmd.label;"
              accesskey="&pasteAsQuotationCmd.accesskey;"
@@ -303,17 +304,20 @@
       <menuitem id="menu_paste"/>
       <menuitem id="menu_pasteNoFormatting"
                 key="pastenoformattingkb"
                 command="cmd_pasteNoFormatting"/>
       <menuitem id="menu_delete"/>
       <menuseparator id="sep_selectAll"/>
       <menuitem id="menu_selectAll"/>
       <menuseparator id="sep_find"/>
-      <menuitem id="menu_find" label="&findCmd.label;"/>
+      <menuitem id="menu_find"
+                label="&findBarCmd.label;"/>
+      <menuitem id="menu_findReplace"
+                label="&findReplaceCmd.label;"/>
       <menuitem id="menu_findNext"/>
       <menuitem id="menu_findPrev"/>
       <menuseparator id="sep_checkspelling"/>
       <menuitem id="menu_checkspelling"
                 label="&checkSpellingCmd2.label;"
                 accesskey="&checkSpellingCmd2.accesskey;"
                 key="key_checkspelling"
                 command="cmd_spelling"/>
--- a/editor/ui/locales/en-US/chrome/composer/editorOverlay.dtd
+++ b/editor/ui/locales/en-US/chrome/composer/editorOverlay.dtd
@@ -11,17 +11,18 @@
 <!ENTITY pasteNoFormatting.label "Paste Without Formatting">
 <!ENTITY pasteNoFormatting.accesskey "n">
 <!ENTITY pasteNoFormatting.key "V">
 <!ENTITY pasteAs.label "Paste As">
 <!ENTITY pasteAs.accesskey "a">
 <!ENTITY pasteAsQuotationCmd.label "Paste As Quotation">
 <!ENTITY pasteAsQuotationCmd.accesskey "Q">
 <!ENTITY pasteAsQuotationCmd.key "o">
-<!ENTITY findCmd.label "Find and Replace…">
+<!ENTITY findBarCmd.label "Find…">
+<!ENTITY findReplaceCmd.label "Find and Replace…">
 <!ENTITY enableInlineSpellChecker.label "Spellcheck As You Type">
 <!ENTITY enableInlineSpellChecker.accesskey "S">
 <!ENTITY checkSpellingCmd2.label "Check Spelling…">
 <!ENTITY checkSpellingCmd2.accesskey "h">
 <!ENTITY checkSpellingCmd2.key "p">
 
 <!-- Insert menu items -->
 <!ENTITY insertMenu.label "Insert">
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -935,16 +935,25 @@ function CommandUpdate_MsgCompose()
   // we're just setting focus to where it was before
   if (focusedWindow == gLastWindowToHaveFocus)
     return;
 
   gLastWindowToHaveFocus = focusedWindow;
   updateComposeItems();
 }
 
+function findbarFindReplace()
+{
+  SetMsgBodyFrameFocus();
+  let findbar = document.getElementById("FindToolbar");
+  findbar.close();
+  goDoCommand("cmd_findReplace");
+  findbar.open();
+}
+
 function updateComposeItems()
 {
   try {
     // Edit Menu
     goUpdateCommand("cmd_rewrap");
 
     // Insert Menu
     if (gMsgCompose && gMsgCompose.composeHTML)
@@ -1071,16 +1080,17 @@ function updateEditItems()
 {
   goUpdateCommand("cmd_paste");
   goUpdateCommand("cmd_pasteNoFormatting");
   goUpdateCommand("cmd_pasteQuote");
   goUpdateCommand("cmd_delete");
   goUpdateCommand("cmd_renameAttachment");
   goUpdateCommand("cmd_selectAll");
   goUpdateCommand("cmd_openAttachment");
+  goUpdateCommand("cmd_findReplace");
   goUpdateCommand("cmd_find");
   goUpdateCommand("cmd_findNext");
   goUpdateCommand("cmd_findPrev");
 }
 
 function updateAttachmentItems()
 {
   goUpdateCommand("cmd_attachCloud");
@@ -1772,20 +1782,30 @@ function handleMailtoArgs(mailtoUrl)
 
 /**
  * Handle ESC keypress from composition window for
  * notifications with close button in the
  * attachmentNotificationBox.
  */
 function handleEsc()
 {
+  let activeElement = document.activeElement;
+
+  // If findbar is visible and the focus is in the message body,
+  // hide it. (Focus on the findbar is handled by findbar itself).
+  let findbar = document.getElementById('FindToolbar');
+  if (!findbar.hidden && activeElement.id == "content-frame") {
+    findbar.close();
+    return;
+  }
+
   // If there is a notification in the attachmentNotificationBox
   // AND focus is in message body or on the notification, hide it.
-  let activeElement = document.activeElement;
-  let notification = document.getElementById("attachmentNotificationBox").currentNotification;
+  let notification = document.getElementById("attachmentNotificationBox")
+                             .currentNotification;
   if (notification && (activeElement.id == "content-frame" ||
       notification.contains(activeElement) ||
       activeElement.classList.contains("messageCloseButton"))) {
     notification.close();
   }
 }
 
 var attachmentWorker = new Worker("resource:///modules/attachmentChecker.js");
@@ -1989,16 +2009,34 @@ function CheckForAttachmentNotification(
   if (notification && removeNotification)
     nBox.removeNotification(notification);
 };
 
 CheckForAttachmentNotification.shouldFire = true;
 
 function ComposeStartup(recycled, aParams)
 {
+  // Findbar overlay
+  if (!document.getElementById("findbar-replaceButton")) {
+    let replaceButton = document.createElement("toolbarbutton");
+    replaceButton.setAttribute("id", "findbar-replaceButton");
+    replaceButton.setAttribute("class", "tabbable");
+    replaceButton.setAttribute("label", getComposeBundle().getString("replaceButton.label"));
+    replaceButton.setAttribute("accesskey", getComposeBundle().getString("replaceButton.accesskey"));
+    replaceButton.setAttribute("tooltiptext", getComposeBundle().getString("replaceButton.tooltip"));
+    replaceButton.setAttribute("oncommand", "findbarFindReplace();");
+
+    let findbar = document.getElementById("FindToolbar");
+    let lastButton = findbar.getElement("find-case-sensitive");
+    let tSeparator = document.createElement("toolbarseparator");
+    tSeparator.setAttribute("id", "findbar-beforeReplaceSeparator");
+    lastButton.parentNode.insertBefore(replaceButton, lastButton.nextSibling);
+    lastButton.parentNode.insertBefore(tSeparator, lastButton.nextSibling);
+  }
+
   var params = null; // New way to pass parameters to the compose window as a nsIMsgComposeParameters object
   var args = null;   // old way, parameters are passed as a string
 
   if (aParams)
     params = aParams;
   else if (window.arguments && window.arguments[0]) {
     try {
       if (window.arguments[0] instanceof Components.interfaces.nsIMsgComposeParams)
--- a/mail/components/compose/content/editorOverlay.xul
+++ b/mail/components/compose/content/editorOverlay.xul
@@ -93,16 +93,17 @@
   <commandset id="composerEditMenuItems"
           commandupdater="true"
           events="create, mode_switch"
           oncommandupdate="goUpdateComposerMenuItems(this)"
   >
     <command id="cmd_pasteNoFormatting" oncommand="goDoCommand('cmd_pasteNoFormatting')"
              label="&pasteNoFormatting.label;" accesskey="&pasteNoFormatting.accesskey;"/>
     <command id="cmd_preferences"   oncommand="goDoCommand('cmd_preferences')"/>
+    <command id="cmd_findReplace"   oncommand="goDoCommand('cmd_findReplace')"/>
     <command id="cmd_find"          oncommand="goDoCommand('cmd_find')"/>
     <command id="cmd_findNext"      oncommand="goDoCommand('cmd_findNext');"/>
     <command id="cmd_findPrev"      oncommand="goDoCommand('cmd_findPrev');"/>
     <command id="cmd_spelling"      oncommand="goDoCommand('cmd_spelling')"/>
     <command id="cmd_pasteQuote"    oncommand="goDoCommand('cmd_pasteQuote')" label="&pasteAsQuotationCmd.label;"/>
   </commandset>
 
   <!-- style related commands that update on creation, and on selection change -->
--- a/mail/components/compose/content/messengercompose.xul
+++ b/mail/components/compose/content/messengercompose.xul
@@ -216,19 +216,19 @@
 #ifdef XP_MACOSX
   <key id="key_delete" keycode="VK_BACK" command="cmd_delete"/>
   <key id="key_delete2" keycode="VK_DELETE" command="cmd_delete"/>
 #else
   <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
   <key id="key_renameAttachment" keycode="VK_F2" oncommand="goDoCommand('cmd_renameAttachment');"/>
 #endif
   <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
-  <key id="key_find" key="&findCmd.key;" command="cmd_find" modifiers="accel"/>
+  <key id="key_find" key="&findBarCmd.key;" command="cmd_find" modifiers="accel"/>
 #ifndef XP_MACOSX
-  <key id="key_findReplace" key="&findReplaceCmd.key;" command="cmd_find" modifiers="accel"/>
+  <key id="key_findReplace" key="&findReplaceCmd.key;" command="cmd_findReplace" modifiers="accel"/>
 #endif
   <key id="key_findNext" key="&findAgainCmd.key;" command="cmd_findNext" modifiers="accel"/>
   <key id="key_findPrev" key="&findPrevCmd.key;" command="cmd_findPrev" modifiers="accel, shift"/>
   <key keycode="&findAgainCmd.key2;" command="cmd_findNext"/>
   <key keycode="&findPrevCmd.key2;"  command="cmd_findPrev" modifiers="shift"/>
 
   <!-- View Menu -->
   <key id="key_addressSidebar" keycode="VK_F9" oncommand="toggleAddressPicker();"/>
@@ -513,19 +513,43 @@
           <menuitem id="menu_rewrap" label="&editRewrapCmd.label;" key="key_rewrap" accesskey="&editRewrapCmd.accesskey;" command="cmd_rewrap"/>
           <menuitem id="menu_RenameAttachment"
                     label="&renameAttachmentCmd.label;"
                     accesskey="&renameAttachmentCmd.accesskey;"
                     key="key_renameAttachment" command="cmd_renameAttachment"/>
           <menuseparator/>
           <menuitem id="menu_selectAll" label="&selectAllCmd.label;" key="key_selectAll" accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"/>
           <menuseparator/>
-          <menuitem label="&findCmd.label;"      key="key_find"      accesskey="&findCmd.accesskey;"      command="cmd_find"/>
-          <menuitem label="&findAgainCmd.label;" key="key_findNext" accesskey="&findAgainCmd.accesskey;" command="cmd_findNext"/>
-          <menuitem label="&findPrevCmd.label;"  key="key_findPrev" accesskey="&findPrevCmd.accesskey;"  command="cmd_findPrev"/>
+          <menuitem id="menu_findBar"
+                    label="&findBarCmd.label;"
+                    accesskey="&findBarCmd.accesskey;"
+                    key="key_find"
+                    command="cmd_find"/>
+#ifndef XP_MACOSX
+          <menuitem id="menu_findReplace"
+                    label="&findReplaceCmd.label;"
+                    accesskey="&findReplaceCmd.accesskey;"
+                    key="key_findReplace"
+                    command="cmd_findReplace"/>
+#else
+          <menuitem id="menu_findReplace"
+                    label="&findReplaceCmd.label;"
+                    accesskey="&findReplaceCmd.accesskey;"
+                    command="cmd_findReplace"/>
+#endif
+          <menuitem id="menu_findNext"
+                    label="&findAgainCmd.label;"
+                    accesskey="&findAgainCmd.accesskey;"
+                    key="key_findNext"
+                    command="cmd_findNext"/>
+          <menuitem id="menu_findPrev"
+                    label="&findPrevCmd.label;"
+                    accesskey="&findPrevCmd.accesskey;"
+                    key="key_findPrev"
+                    command="cmd_findPrev"/>
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
           <menuseparator id="prefSep"/>
           <menuitem id="menu_accountmgr"
                     label="&accountManagerCmd2.label;"
                     accesskey="&accountManagerCmdUnix2.accesskey;"
                     command="cmd_account"/>
           <menuitem id="menu_preferences" label="&preferencesCmdUnix.label;" accesskey="&preferencesCmdUnix.accesskey;"
@@ -1021,16 +1045,17 @@
 
   <splitter id="compose-toolbar-sizer" onmousedown="awSizerListen()"/>
 
   <!-- The mail message body frame -->
   <vbox id="appcontent" flex="1">
     <editor type="content-primary" id="content-frame" src="about:blank" name="browser.message.body" flex="1"
             ondblclick="EditorDblClick(event);"
             context="msgComposeContext"/>
+    <findbar id="FindToolbar" browserid="content-frame"/>
   </vbox>
 
   </vbox>
   </hbox>
   <panel id="customizeToolbarSheetPopup" noautohide="true">
     <iframe id="customizeToolbarSheetIFrame"
             style="&dialog.dimensions;"
             hidden="true"/>
--- a/mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
+++ b/mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
@@ -406,8 +406,12 @@ cloudAttachmentListFooter=%1$S makes it 
 ## %3$S is the name of the cloud storage service, and %4$S is the link to the
 ## attachment.
 cloudAttachmentListItem=* %1$S (%2$S) hosted on %3$S: %4$S
 
 ## LOCALIZATION NOTE(stopShowingUploadingNotification): This string is used in the Filelink
 ## upload notification bar to allow the user to dismiss the notification permanently.
 stopShowingUploadingNotification.accesskey=N
 stopShowingUploadingNotification.label=Never show this again
+
+replaceButton.label=Replace…
+replaceButton.accesskey=l
+replaceButton.tooltip=Show the Find and Replace dialog
--- a/mail/locales/en-US/chrome/messenger/messengercompose/messengercompose.dtd
+++ b/mail/locales/en-US/chrome/messenger/messengercompose/messengercompose.dtd
@@ -83,28 +83,30 @@
 <!ENTITY deleteCmd.accesskey "d">
 <!ENTITY editRewrapCmd.label "Rewrap">
 <!ENTITY editRewrapCmd.key "R">
 <!ENTITY renameAttachmentCmd.label "Rename Attachment…">
 <!ENTITY renameAttachmentCmd.accesskey "e">
 <!ENTITY selectAllCmd.label "Select All">
 <!ENTITY selectAllCmd.key "A">
 <!ENTITY selectAllCmd.accesskey "a">
-<!ENTITY findCmd.label "Find and Replace…">
-<!ENTITY findCmd.key "F">
-<!ENTITY findCmd.accesskey "F">
+<!ENTITY findBarCmd.label "Find…">
+<!ENTITY findBarCmd.accesskey "F">
+<!ENTITY findBarCmd.key "F">
+<!ENTITY findReplaceCmd.label "Find and Replace…">
+<!ENTITY findReplaceCmd.accesskey "l">
 <!ENTITY findReplaceCmd.key "H">
 <!ENTITY findAgainCmd.label "Find Again">
+<!ENTITY findAgainCmd.accesskey "g">
 <!ENTITY findAgainCmd.key "G">
-<!ENTITY findAgainCmd.accesskey "g">
 <!ENTITY findAgainCmd.key2 "VK_F3">
 <!ENTITY findPrevCmd.label "Find Previous">
+<!ENTITY findPrevCmd.accesskey "v">
 <!ENTITY findPrevCmd.key "G">
 <!ENTITY findPrevCmd.key2 "VK_F3">
-<!ENTITY findPrevCmd.accesskey "v">
 
 <!-- View Menu -->
 <!ENTITY viewMenu.label "View">
 <!ENTITY viewMenu.accesskey "v">
 <!ENTITY viewToolbarsMenuNew.label "Toolbars">
 <!ENTITY viewToolbarsMenuNew.accesskey "T">
 <!ENTITY menubarCmd.label "Menu Bar">
 <!ENTITY menubarCmd.accesskey "M">
--- a/mail/themes/shared/mail/messenger.css
+++ b/mail/themes/shared/mail/messenger.css
@@ -84,8 +84,11 @@ notification[value="addon-install-failed
   animation: highlight .4s ease-in;
 }
 
 @keyframes highlight {
   from { background-color: Highlight; }
   to { background-color: transparent; }
 }
 
+#findbar-beforeReplaceSeparator {
+  height: 16px;
+}
--- a/mail/themes/windows/mail/compose/messengercompose-aero.css
+++ b/mail/themes/windows/mail/compose/messengercompose-aero.css
@@ -1030,8 +1030,12 @@ toolbarbutton.formatting-button {
 @media (-moz-windows-default-theme) {
   #status-bar {
     -moz-appearance: none;
     border: none;
     border-top: 1px solid threedshadow;
     background-color: hsl(210, 75%, 92%);
   }
 }
+
+#findbar-replaceButton > .toolbarbutton-icon {
+  display: none;
+}
--- a/mail/themes/windows/mail/compose/messengercompose.css
+++ b/mail/themes/windows/mail/compose/messengercompose.css
@@ -1004,8 +1004,12 @@ toolbarbutton.formatting-button {
 
 #sidebar {
   background-color: Window;
 }
 
 #sidebar-splitter {
   background-color: ThreeDFace;
 }
+
+#findbar-replaceButton > .toolbarbutton-icon {
+  display: none;
+}
--- a/suite/common/unix/platformCommunicatorOverlay.xul
+++ b/suite/common/unix/platformCommunicatorOverlay.xul
@@ -27,26 +27,31 @@
       <menuseparator id="menu_FileQuitSeparator"/>
       <menuitem label="&quitApplicationCmd.label;" id="menu_FileQuitItem"
                 key="key_quit" accesskey="&quitApplicationCmd.accesskey;" command="cmd_quit"/>
     </menupopup>
     <key id="key_quit"  key="&quitApplicationCmd.key;" command="cmd_quit" modifiers="accel"/>
   
     <!-- Edit Menu -->
     <key id="key_redo"  key="&redoCmd.key;" command="cmd_redo" modifiers="accel"/>
+    <menuitem id="menu_findReplace" key="key_findReplace"/>
 
     <!-- Select All Key -->
     <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="alt"/>
 	
 	<!-- Delete Key -->
   <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
 
   <keyset id="findKeys">
     <key keycode="&findCmd.key2;"
          command="cmd_find"/>
+    <key id="key_findReplace"
+         key="&findReplaceCmd.key;"
+         modifiers="accel"
+         command="cmd_findReplace"/>
   </keyset>
   <!-- Find As You Type Keys -->
   <key id="key_findTypeText"
        key="&findTypeTextCmd.key;"/>
   <key id="key_findTypeLinks"
        key="&findTypeLinksCmd.key;"/>
 
   <key id="showHideSidebar" keycode="VK_F9"/>
--- a/suite/common/utilityOverlay.xul
+++ b/suite/common/utilityOverlay.xul
@@ -223,17 +223,17 @@
        modifiers="accel, shift"/>
   <key id="key_print"
        key="&printCmd.key;"
        command="cmd_print"
        modifiers="accel"/>
 
   <keyset id="findKeys">
     <key id="key_find"
-         key="&findCmd.key;"
+         key="&findBarCmd.key;"
          command="cmd_find"
          modifiers="accel"/>
     <key id="key_findNext"
          key="&findAgainCmd.key;"
          command="cmd_findNext"
          modifiers="accel"/>
     <key id="key_findPrev"
          key="&findPrevCmd.key;"
@@ -275,19 +275,22 @@
             accesskey="&deleteCmd.accesskey;"
             command="cmd_delete"/>
   <menuitem id="menu_selectAll"
             label="&selectAllCmd.label;"
             key="key_selectAll"
             accesskey="&selectAllCmd.accesskey;"
             command="cmd_selectAll"/>
   <menuitem id="menu_find"
-            accesskey="&findCmd.accesskey;"
+            accesskey="&findBarCmd.accesskey;"
             key="key_find"
             command="cmd_find"/>
+  <menuitem id="menu_findReplace"
+            accesskey="&findReplaceCmd.accesskey;"
+            command="cmd_findReplace"/>
   <menuitem id="menu_findNext"
             label="&findAgainCmd.label;"
             accesskey="&findAgainCmd.accesskey;"
             key="key_findNext"
             command="cmd_findNext"/>
   <menuitem id="menu_findPrev"
             label="&findPrevCmd.label;"
             accesskey="&findPrevCmd.accesskey;"
--- a/suite/common/win/platformCommunicatorOverlay.xul
+++ b/suite/common/win/platformCommunicatorOverlay.xul
@@ -27,22 +27,32 @@
     <menuseparator id="menu_FileQuitSeparator"/>
     <menuitem label="&quitApplicationCmd.label;" id="menu_FileQuitItem"
               key="key_quit" accesskey="&quitApplicationCmd.accesskey;" command="cmd_quit"/>
   </menupopup>
   <key id="key_quit"  key="&quitApplicationCmd.key;" command="cmd_quit" modifiers="accel"/>
   
   <!-- Edit Menu -->
   <key id="key_redo"  key="&redoCmd.key;" command="cmd_redo" modifiers="accel"/>
+  <menuitem id="menu_findReplace" key="key_findReplace"/>
+
   <!-- Select All Key -->
   <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
   
   <!-- Delete Key -->
   <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
 
+  <!-- Keyset for find keys -->
+  <keyset id="findKeys">
+    <key id="key_findReplace"
+         key="&findReplaceCmd.key;"
+         modifiers="accel"
+         command="cmd_findReplace"/>
+  </keyset>
+
   <!-- Find As You Type Keys -->
   <key id="key_findTypeText"
        key="&findTypeTextCmd.key;"/>
   <key id="key_findTypeLinks"
        key="&findTypeLinksCmd.key;"/>
 
   <key id="showHideSidebar" keycode="VK_F9"/>
 
--- a/suite/locales/en-US/chrome/common/utilityOverlay.dtd
+++ b/suite/locales/en-US/chrome/common/utilityOverlay.dtd
@@ -64,21 +64,23 @@
 <!ENTITY selectAllCmd.accesskey           "A">
 <!ENTITY clearHistoryCmd.label				"Clear Search History">  
 <!ENTITY clearHistoryCmd.accesskey			"H"> 
 <!ENTITY showSuggestionsCmd.label			"Show Suggestions">  
 <!ENTITY showSuggestionsCmd.accesskey		"S"> 
 <!ENTITY preferencesCmd.label				"Preferences…">
 <!ENTITY preferencesCmd.key					"E">  
 <!ENTITY preferencesCmd.accesskey			"e"> 
-<!ENTITY findCmd.key "F">
-<!-- LOCALIZATION NOTE (findCmd.accesskey): This accesskey should be within
-     findCmd.label found in messengercompose.dtd, messenger.dtd and
-     editorOverlay.dtd and findOnCmd.label found in navigatorOverlay.dtd -->
-<!ENTITY findCmd.accesskey "F">
+<!ENTITY findBarCmd.key "F">
+<!-- LOCALIZATION NOTE (findBarCmd.accesskey): This accesskey should be within
+     findBarCmd.label found in editorOverlay.dtd, findCmd.label in messenger.dtd
+     and messengercompose.dtd and findOnCmd.label found in navigatorOverlay.dtd -->
+<!ENTITY findBarCmd.accesskey "F">
+<!ENTITY findReplaceCmd.key "H">
+<!ENTITY findReplaceCmd.accesskey "l">
 <!ENTITY findAgainCmd.label "Find Again">
 <!ENTITY findAgainCmd.key "G">
 <!ENTITY findAgainCmd.accesskey "g">
 <!ENTITY findAgainCmd.key2 "VK_F3">
 <!ENTITY findPrevCmd.label "Find Previous">
 <!ENTITY findPrevCmd.key "G">
 <!ENTITY findPrevCmd.key2 "VK_F3">
 <!ENTITY findPrevCmd.accesskey "v">
--- a/suite/mailnews/compose/MsgComposeCommands.js
+++ b/suite/mailnews/compose/MsgComposeCommands.js
@@ -629,16 +629,17 @@ function openEditorContextMenu(popup)
 function updateEditItems()
 {
   goUpdateCommand("cmd_pasteNoFormatting");
   goUpdateCommand("cmd_pasteQuote");
   goUpdateCommand("cmd_delete");
   goUpdateCommand("cmd_renameAttachment");
   goUpdateCommand("cmd_selectAll");
   goUpdateCommand("cmd_openAttachment");
+  goUpdateCommand("cmd_findReplace");
   goUpdateCommand("cmd_find");
   goUpdateCommand("cmd_findNext");
   goUpdateCommand("cmd_findPrev");
 }
 
 function updateOptionItems()
 {
   goUpdateCommand("cmd_quoteMessage");
--- a/suite/mailnews/compose/messengercompose.xul
+++ b/suite/mailnews/compose/messengercompose.xul
@@ -97,16 +97,17 @@
   <command id="cmd_sendWithCheck" oncommand="goDoCommand('cmd_sendWithCheck')"/>
   <command id="cmd_sendLater" oncommand="goDoCommand('cmd_sendLater')"/>
   <command id="cmd_printpreview" disabled="true"/>
 
   <!-- Edit Menu -->
   <!--command id="cmd_pasteQuote"/  DO NOT INCLUDE THOSE COMMANDS ELSE THE EDIT MENU WILL BE BROKEN! -->
   <!--command id="cmd_find"/-->
   <!--command id="cmd_findNext"/-->
+  <!--command id="cmd_findReplace"/-->
   <command id="cmd_rewrap"
            label="&editRewrapCmd.label;"
            accesskey="&editRewrapCmd.accesskey;"
            oncommand="goDoCommand('cmd_rewrap');"/>
   <command id="cmd_renameAttachment" oncommand="goDoCommand('cmd_renameAttachment')" disabled="true"/>
   <command id="cmd_openAttachment" oncommand="goDoCommand('cmd_openAttachment')"/>
   <command id="cmd_account"
            label="&accountManagerCmd.label;"
@@ -625,16 +626,17 @@
 <!-- sidebar/toolbar/content/status -->
 <hbox id="sidebar-parent" flex="1">
   <!-- From sidebarOverlay.xul -->
   <vbox id="sidebar-box" class="chromeclass-extrachrome" hidden="true"/>
   <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
 
   <!-- The mail message body frame -->
   <vbox id="appcontent" flex="1">
+    <findbar id="FindToolbar" browserid="content-frame"/>
     <editor type="content-primary" id="content-frame" src="about:blank" name="browser.message.body" flex="1"
             ondblclick="EditorDblClick(event);"
             context="contentAreaContextMenu"/>
   </vbox>
 </hbox>
 
   <statusbar id="status-bar" class="chromeclass-status">
     <statusbarpanel id="component-bar"/>