[Pkg-mozext-commits] [compactheader] 235/441: Reorganized toolbar functions. Created first set of mozmill tests.

David Prévot taffit at moszumanska.debian.org
Wed Mar 18 12:29:02 UTC 2015


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository compactheader.

commit 7175d10056c3d5cce1861ccf45fe8dda7171e9ed
Author: joachim <none at none>
Date:   Sat Jul 30 16:22:56 2011 +0200

    Reorganized toolbar functions.
    Created first set of mozmill tests.
    
    --HG--
    branch : mozmill
---
 .hgignore                                          |   6 +-
 chrome/CompactHeader/content/CHTMessenger.js       |  71 +-
 .../CompactHeader/content/compactHeaderOverlay.js  |  30 +-
 .../CompactHeader/content/compactHeaderOverlay.xul |  36 +-
 .../content/customizeToolbarOverlay.xul            |   4 +-
 chrome/CompactHeader/content/toolbar.js            |  77 +-
 test/compactheader/test-compactheader-toolbar.js   | 604 +++++++++++++++
 test/compactheader/test-message-header.js          | 837 +++++++++++++++++++++
 test/compactheader/test-mouse-event-helpers.js     | 220 ++++++
 test/download.sh                                   |  20 +
 10 files changed, 1801 insertions(+), 104 deletions(-)

diff --git a/.hgignore b/.hgignore
index 5b87d2a..32e23f8 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,3 +1,7 @@
 
 syntax: regexp
-^AMO$
\ No newline at end of file
+^AMO$
+syntax: regexp
+^test/ftp$
+syntax: regexp
+^test/test-.*
\ No newline at end of file
diff --git a/chrome/CompactHeader/content/CHTMessenger.js b/chrome/CompactHeader/content/CHTMessenger.js
index fcac6fb..8d230db 100644
--- a/chrome/CompactHeader/content/CHTMessenger.js
+++ b/chrome/CompactHeader/content/CHTMessenger.js
@@ -49,12 +49,12 @@ org.mozdev.customizeHeaderToolbar.messenger = function(){
 //    var onLoadFkt = document.getElementById("messengerWindow").getAttribute("onload");
 //    if (onLoadFkt) {
 //      var strTest = new RegExp('OnLoadMessenger', 'g');;
-//      onLoadFkt = onLoadFkt.replace(strTest, 
+//      onLoadFkt = onLoadFkt.replace(strTest,
 //        "org.mozdev.customizeHeaderToolbar.messenger.CHTLoadMessenger");
 //      document.getElementById("messengerWindow").setAttribute("onload", onLoadFkt);
 //    }
 //  }
-  
+
   pub.saveToolboxData = function() {
     var hdrToolbox = document.getElementById("header-view-toolbox");
     var hdrToolbar = document.getElementById("header-view-toolbar");
@@ -89,7 +89,7 @@ org.mozdev.customizeHeaderToolbar.messenger = function(){
       saveToolbox.setAttribute("gotData", "false");
     }
   }
-  
+
   pub.loadToolboxData = function() {
     var hdrToolbox = document.getElementById("header-view-toolbox");
     var hdrToolbar = document.getElementById("header-view-toolbar");
@@ -106,72 +106,9 @@ org.mozdev.customizeHeaderToolbar.messenger = function(){
         hdrToolbox.toolbarset = saveToolbox.toolbarset.cloneNode(true);
       } else {
       }
-    } 
+    }
   }
-  
-
-  return pub;
-}();
 
-org.mozdev.customizeHeaderToolbar.pane = function(){
-  var pub = {};
-  
-  pub.CHTUpdateReplyButton = function () {
-    UpdateReplyButtons();
-  }
-  
-  pub.CHTUpdateJunkButton = function () {
-    UpdateJunkButton();    
-  }
-  
-  pub.CHTSetDefaultButtons = function () {
-    var hdrToolbox = document.getElementById("header-view-toolbox");
-    var hdrToolbar = document.getElementById("header-view-toolbar");
-    var hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
-    var hdrBoxDefaultLabelalign = hdrToolbox.getAttribute("defaultlabelalign");
-    var hdrBoxDefaultIconsize = hdrToolbox.getAttribute("defaulticonsize");
-    var hdrBoxDefaultMode = hdrToolbox.getAttribute("defaultmode");
-    var hdrBarDefaultIconsize = hdrToolbar.getAttribute("defaulticonsize");
-    var hdrBarDefaultMode = hdrToolbar.getAttribute("defaultmode");  
-    
-    hdrToolbox.setAttribute("labelalign", hdrBoxDefaultLabelalign);
-    hdrToolbox.setAttribute("iconsize", hdrBoxDefaultIconsize);
-    hdrToolbox.setAttribute("mode", hdrBoxDefaultMode);
-    hdrToolbar.setAttribute("iconsize", hdrBarDefaultIconsize);
-    hdrToolbar.setAttribute("mode", hdrBarDefaultMode);
-    
-    hdrToolbar.currentSet = hdrBarDefaultSet;
-    hdrToolbar.setAttribute("currentset", hdrBarDefaultSet);
-    
-    document.persist(hdrToolbox.id,"labelalign");
-    document.persist(hdrToolbox.id,"iconsize");
-    document.persist(hdrToolbox.id,"mode");
-    document.persist(hdrToolbar.id,"iconsize");
-    document.persist(hdrToolbar.id,"mode");
-    document.persist(hdrToolbar.id,"currentset");
-  }
-  
-  pub.CHTCleanupButtons = function() {
-    var hdrToolbox = document.getElementById("header-view-toolbox");
-    var hdrToolbar = document.getElementById("header-view-toolbar");
-    var hdrBarDefaultSet = "hdrReplyToSenderButton,hdrSmartReplyButton,hdrForwardButton,hdrArchiveButton,hdrJunkButton,hdrTrashButton";
-    
-    hdrToolbox.setAttribute("labelalign", "end");
-    hdrToolbox.setAttribute("iconsize", "small");
-    hdrToolbox.setAttribute("mode", "full");
-    
-    hdrToolbar.setAttribute("iconsize", "small");
-    hdrToolbar.setAttribute("mode", "full");
-    hdrToolbar.currentSet = hdrBarDefaultSet;
-    hdrToolbar.setAttribute("currentset", hdrBarDefaultSet);
-    
-    document.persist(hdrToolbox.id,"labelalign");
-    document.persist(hdrToolbox.id,"iconsize");
-    document.persist(hdrToolbox.id,"mode");
-    document.persist(hdrToolbar.id,"iconsize");
-    document.persist(hdrToolbar.id,"mode");
-    document.persist(hdrToolbar.id,"currentset");
-  }
 
   return pub;
 }();
diff --git a/chrome/CompactHeader/content/compactHeaderOverlay.js b/chrome/CompactHeader/content/compactHeaderOverlay.js
index c490cfc..634240b 100644
--- a/chrome/CompactHeader/content/compactHeaderOverlay.js
+++ b/chrome/CompactHeader/content/compactHeaderOverlay.js
@@ -246,8 +246,8 @@ org.mozdev.compactHeader.pane = function() {
         MailToolboxCustomizeDone(aEvent, "CustomizeHeaderToolbar");
         document.getElementById("header-view-toolbox").removeAttribute("doCustomization");
         enableButtons();
-        org.mozdev.customizeHeaderToolbar.pane.CHTUpdateReplyButton();
-        org.mozdev.customizeHeaderToolbar.pane.CHTUpdateJunkButton();
+        org.mozdev.compactHeader.toolbar.CHTUpdateReplyButton();
+        org.mozdev.compactHeader.toolbar.CHTUpdateJunkButton();
         org.mozdev.compactHeader.buttons.coheToggleStar();
         org.mozdev.customizeHeaderToolbar.messenger.saveToolboxData();
       };
@@ -344,8 +344,8 @@ org.mozdev.compactHeader.pane = function() {
 
     //org.mozdev.compactHeader.toolbar.fillToolboxPalette(document);
     coheToggleHeaderContent();
-    org.mozdev.customizeHeaderToolbar.pane.CHTUpdateReplyButton();
-    org.mozdev.customizeHeaderToolbar.pane.CHTUpdateJunkButton();
+    org.mozdev.compactHeader.toolbar.CHTUpdateReplyButton();
+    org.mozdev.compactHeader.toolbar.CHTUpdateJunkButton();
     org.mozdev.compactHeader.buttons.coheToggleStar();
   }
 
@@ -590,7 +590,8 @@ org.mozdev.compactHeader.pane = function() {
                                             .getService(Components.interfaces.nsIVersionComparator);
 //    org.mozdev.compactHeader.debug.log("first run 0");
     if(versionChecker.compare(appInfo.version, "3.2a1pre") < 0) {
-//      org.mozdev.compactHeader.debug.log("firstrun 1");
+      org.mozdev.compactHeader.debug.log("firstrun 1");
+      org.mozdev.compactHeader.toolbar.populateEmptyToolbar();
       cohe.version = -1;
       cohe.firstrun = false;
       cohe.gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager);
@@ -611,7 +612,7 @@ org.mozdev.compactHeader.pane = function() {
         if (cohe.firstrun){
           cohePrefBranch.setBoolPref("firstrun",false);
           cohePrefBranch.setCharPref("version",cohe.current);
-          org.mozdev.customizeHeaderToolbar.pane.CHTSetDefaultButtons();
+          org.mozdev.compactHeader.toolbar.CHTSetDefaultButtons();
         }
         //check for upgrade
         if (cohe.version!=cohe.current && !cohe.firstrun){
@@ -624,12 +625,13 @@ org.mozdev.compactHeader.pane = function() {
       }
     }
     else {
-      //org.mozdev.compactHeader.debug.log("firstrun 3");
+      org.mozdev.compactHeader.debug.log("firstrun 3");
+      org.mozdev.compactHeader.toolbar.populateEmptyToolbar();
       Components.utils.import("resource://gre/modules/AddonManager.jsm");
       AddonManager.getAddonByID(COHE_EXTENSION_UUID,
         function(myAddon) {
-//          org.mozdev.compactHeader.debug.log("first run 2");
-          cohe.version = -1;
+          org.mozdev.compactHeader.debug.log("first run 2");
+          cohe.version = "";
           cohe.firstrun = false;
           cohe.current = myAddon.version;
           try{
@@ -639,14 +641,16 @@ org.mozdev.compactHeader.pane = function() {
           } finally {
             //check for first run
             if (cohe.firstrun){
-//              org.mozdev.compactHeader.debug.log("first run 2c");
-              org.mozdev.customizeHeaderToolbar.pane.CHTSetDefaultButtons();
+              org.mozdev.compactHeader.debug.log("first run 2c");
+              org.mozdev.compactHeader.toolbar.CHTSetDefaultButtons();
               cohePrefBranch.setBoolPref("firstrun",false);
               cohePrefBranch.setCharPref("version",cohe.current);
+              org.mozdev.compactHeader.debug.log("first run 2cc");
             }
             //check for upgrade
             if (cohe.version!=cohe.current && !cohe.firstrun){
               cohePrefBranch.setCharPref("version",cohe.current);
+              org.mozdev.compactHeader.debug.log("found version change");
               // XXX
             }
             cohe.firstrun = false;
@@ -655,7 +659,7 @@ org.mozdev.compactHeader.pane = function() {
         }
       );
     }
-//    org.mozdev.compactHeader.debug.log("firstrun 4");
+    org.mozdev.compactHeader.debug.log("firstrun 4");
   }
 
 
@@ -688,7 +692,7 @@ org.mozdev.compactHeader.pane = function() {
         org.mozdev.compactHeader.debug.log("uninstalling COHE 2");
         if (this._uninstall) {
           cohePrefBranch.deleteBranch("");
-          org.mozdev.customizeHeaderToolbar.pane.CHTCleanupButtons();
+          org.mozdev.compactHeader.toolbar.CHTCleanupButtons();
         }
         this.unregister();
       }
diff --git a/chrome/CompactHeader/content/compactHeaderOverlay.xul b/chrome/CompactHeader/content/compactHeaderOverlay.xul
index fd01513..7b75974 100644
--- a/chrome/CompactHeader/content/compactHeaderOverlay.xul
+++ b/chrome/CompactHeader/content/compactHeaderOverlay.xul
@@ -1,8 +1,8 @@
 <?xml version="1.0" ?>
 
 <!DOCTYPE overlay [
-  <!ENTITY % msgHeaderDTD SYSTEM 
-  "chrome://messenger/locale/msgHdrViewOverlay.dtd"> 
+  <!ENTITY % msgHeaderDTD SYSTEM
+  "chrome://messenger/locale/msgHdrViewOverlay.dtd">
   %msgHeaderDTD;
   <!ENTITY % compactHeaderDTD SYSTEM
   "chrome://CompactHeader/locale/CompactHeader.dtd">
@@ -25,19 +25,17 @@
   <script type="application/javascript" src="chrome://CompactHeader/content/compactHeaderOverlay.js"/>
   <script type="application/javascript" src="chrome://CompactHeader/content/RSSLinkify.js"/>
   <script type="application/javascript" src="chrome://CompactHeader/content/toolbar.js"/>
-  <script type="application/x-javascript" 
-          src="chrome://CompactHeader/content/CHTMessenger.js"/>
   <script type="application/javascript" src="chrome://CompactHeader/content/buttonsOverlay.js"/>
 
   <keyset>
-    <key id="hideDetailsKey" 
-         modifiers="shift" 
+    <key id="hideDetailsKey"
+         modifiers="shift"
          key="&toggleDetails.key;"
          oncommand="org.mozdev.compactHeader.pane.coheToggleHeaderView();"/>
   </keyset>
 
   <menupopup id="header-toolbar-context-menu">
-    <menuitem id="hidecohePreferencesButton" 
+    <menuitem id="hidecohePreferencesButton"
               label="&dialog.title;"
               oncommand="openDialog('chrome://CompactHeader/content/preferences.xul', 'prefs', 'chrome,resizable=no,centerscreen,modal');"/>
   </menupopup>
@@ -46,9 +44,9 @@
 
   <vbox id="expandedHeaderView" orient="horizontal">
     <vbox id="hideDetailsButtonBox" insertbefore="expandedHeadersBox">
-      <button id="hideDetailsButton"  
+      <button id="hideDetailsButton"
               tooltiptext="&hideDetailsButton.label;"
-              onclick="org.mozdev.compactHeader.pane.coheToggleHeaderView();" 
+              onclick="org.mozdev.compactHeader.pane.coheToggleHeaderView();"
               class="msgHeaderView-flat-button"/>
     </vbox>
 
@@ -57,7 +55,7 @@
       <label id="CoheShowDetailsLabel" value="&showDetailsButton.label;"/>
     </hbox>
 
-    <vbox id="expandedHeadersBox">  
+    <vbox id="expandedHeadersBox">
       <hbox id="expandedHeadersTopBox" flex="1">
         <toolbox id="header-view-toolbox"
                  mode="icons"
@@ -71,14 +69,14 @@
           </toolbarpalette>
           <toolbar id="header-view-toolbar"
                    mode="icons" defaultmode="icons"
-                   defaultset="hdrReplyToSenderButton,hdrReplyButton,hdrReplyAllButton,hdrReplyListButton,hdrForwardButton,button-reply,button-forward,hdrArchiveButton,hdrJunkButton,hdrTrashButton,hdrOtherActionsButton">
+                   defaultset="hdrReplyToSenderButton,hdrForwardButton,button-reply,button-forward,hdrArchiveButton,hdrJunkButton,hdrTrashButton,hdrOtherActionsButton">
           </toolbar>
         </toolbox>
       </hbox>
 
 
       <hbox id="expandedHeadersBottomBox">
-      
+
         <vbox id="otherActionsBox" flex="0">
           <hbox id="dateValueBox" flex="0"/>
           <button type="menu" id="otherActionsButton" collapsed="true"
@@ -109,17 +107,17 @@
                 </menupopup>
               </menu>
               <menuseparator id="otherActionPopupAfterViewSource"/>
-              <menuitem id="hideDetailsMenu" 
+              <menuitem id="hideDetailsMenu"
                         label="&hideDetailsButton.label;"
                         oncommand="org.mozdev.compactHeader.pane.coheToggleHeaderView();"/>
-              <menuitem id="hidecohePreferencesButton" 
+              <menuitem id="hidecohePreferencesButton"
                         label="&dialog.title;"
                         oncommand="openDialog('chrome://CompactHeader/content/preferences.xul', 'prefs', 'chrome,centerscreen,dependent,all,resizable=no');"/>
             </menupopup>
           </button>
         </vbox>
       </hbox>
-      
+
     </vbox>
 
     <vbox id="dispMUAexp" collapsed="false">
@@ -165,7 +163,7 @@
             <column flex="1"/>
           </columns>
           <row id="collapsed1LtoCcBccRow" align="baseline">
-            <label id="collapsed1LtoCcBccLabel" class="headerName" 
+            <label id="collapsed1LtoCcBccLabel" class="headerName"
                    value="&toField2.label;" control="collapsed1LtoCcBccBox"/>
              <mail-multi-emailHeaderField id="collapsed1LtoCcBccBox" flex="1"/>
           </row>
@@ -175,7 +173,7 @@
           <row id="collapsed1LdateRow">
             <label id="collapsed1LdateBox" class="dateLabel" flex="1"/>
           </row>
-        </hbox>        
+        </hbox>
         <header-view-button-box id="collapsed1LButtonBox" flex="0" align="start" hidden="true"/>
       </hbox>
       <hbox id="collapsed1LHeaderViewSecondLine" align="top" flex="1" collapsed="true">
@@ -203,7 +201,7 @@
           </columns>
           <rows>
             <row id="collapsed2LtoCcBccRow" align="baseline">
-              <label id="collapsed2LtoCcBccLabel" class="headerName" 
+              <label id="collapsed2LtoCcBccLabel" class="headerName"
                      value="&toField2.label;" control="collapsed2LtoCcBccBox"/>
               <mail-multi-emailHeaderField id="collapsed2LtoCcBccBox" flex="1"/>
             </row>
@@ -243,5 +241,5 @@
 
 </overlay>
 
-<!-- 
+<!--
 -->
\ No newline at end of file
diff --git a/chrome/CompactHeader/content/customizeToolbarOverlay.xul b/chrome/CompactHeader/content/customizeToolbarOverlay.xul
index 39aef22..35246f5 100644
--- a/chrome/CompactHeader/content/customizeToolbarOverlay.xul
+++ b/chrome/CompactHeader/content/customizeToolbarOverlay.xul
@@ -2,7 +2,7 @@
 
 <!DOCTYPE overlay [
 <!ENTITY % customizeToolbarDTD SYSTEM "chrome://global/locale/customizeToolbar.dtd">
-  %customizeToolbarDTD; 
+  %customizeToolbarDTD;
 ]>
 
 <?xml-stylesheet href="chrome://CompactHeader-os/skin/osdef.css" type="text/css"?>
@@ -15,4 +15,6 @@
   <script type="application/javascript" src="chrome://CompactHeader/content/RSSLinkify.js"/>
   <script type="application/javascript" src="chrome://CompactHeader/content/toolbar.js"/>
 
+  <window id="CustomizeToolbarWindow"
+          windowtype="mail:customizetoolbar"/>
 </overlay>
diff --git a/chrome/CompactHeader/content/toolbar.js b/chrome/CompactHeader/content/toolbar.js
index 6e7f167..6e3e7cc 100644
--- a/chrome/CompactHeader/content/toolbar.js
+++ b/chrome/CompactHeader/content/toolbar.js
@@ -418,12 +418,12 @@ org.mozdev.compactHeader.toolbar = function() {
     if (!testnew) {
       el.className += el.className?' '+strClass:strClass;
     }
-  }
+  };
 
   function removeClass(el, strClass) {
     var str = new RegExp('(\\s|^)'+strClass+'(\\s|$)', 'g');
     el.className = el.className.replace(str, ' ');
-  }
+  };
 
   function flatButtons() {
     var result = cohePrefBranch.getBoolPref("headersize.flatButtons");
@@ -431,7 +431,78 @@ org.mozdev.compactHeader.toolbar = function() {
       result = false;
     }
     return result;
-  }
+  };
+
+  pub.CHTUpdateReplyButton = function () {
+    UpdateReplyButtons();
+  };
+
+  pub.CHTUpdateJunkButton = function () {
+    UpdateJunkButton();
+  };
+
+  pub.CHTSetDefaultButtons = function () {
+    var hdrToolbox = document.getElementById("header-view-toolbox");
+    var hdrToolbar = document.getElementById("header-view-toolbar");
+    var hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+    var hdrBoxDefaultLabelalign = hdrToolbox.getAttribute("defaultlabelalign");
+    var hdrBoxDefaultIconsize = hdrToolbox.getAttribute("defaulticonsize");
+    var hdrBoxDefaultMode = hdrToolbox.getAttribute("defaultmode");
+    var hdrBarDefaultIconsize = hdrToolbar.getAttribute("defaulticonsize");
+    var hdrBarDefaultMode = hdrToolbar.getAttribute("defaultmode");
+
+    hdrToolbox.setAttribute("labelalign", hdrBoxDefaultLabelalign);
+    hdrToolbox.setAttribute("iconsize", hdrBoxDefaultIconsize);
+    hdrToolbox.setAttribute("mode", hdrBoxDefaultMode);
+    hdrToolbar.setAttribute("iconsize", hdrBarDefaultIconsize);
+    hdrToolbar.setAttribute("mode", hdrBarDefaultMode);
+
+    hdrToolbar.currentSet = hdrBarDefaultSet;
+    hdrToolbar.setAttribute("currentset", hdrBarDefaultSet);
+
+    document.persist(hdrToolbox.id,"labelalign");
+    document.persist(hdrToolbox.id,"iconsize");
+    document.persist(hdrToolbox.id,"mode");
+    document.persist(hdrToolbar.id,"iconsize");
+    document.persist(hdrToolbar.id,"mode");
+    document.persist(hdrToolbar.id,"currentset");
+  };
+
+  pub.CHTCleanupButtons = function() {
+    var hdrToolbox = document.getElementById("header-view-toolbox");
+    var hdrToolbar = document.getElementById("header-view-toolbar");
+    var hdrBarDefaultSet = "hdrReplyToSenderButton,hdrSmartReplyButton,hdrForwardButton,hdrArchiveButton,hdrJunkButton,hdrTrashButton";
+
+    hdrToolbox.setAttribute("labelalign", "end");
+    hdrToolbox.setAttribute("iconsize", "small");
+    hdrToolbox.setAttribute("mode", "full");
+
+    hdrToolbar.setAttribute("iconsize", "small");
+    hdrToolbar.setAttribute("mode", "full");
+    hdrToolbar.currentSet = hdrBarDefaultSet;
+    hdrToolbar.setAttribute("currentset", hdrBarDefaultSet);
+
+    document.persist(hdrToolbox.id,"labelalign");
+    document.persist(hdrToolbox.id,"iconsize");
+    document.persist(hdrToolbox.id,"mode");
+    document.persist(hdrToolbar.id,"iconsize");
+    document.persist(hdrToolbar.id,"mode");
+    document.persist(hdrToolbar.id,"currentset");
+  };
+
+  pub.populateEmptyToolbar = function() {
+    org.mozdev.compactHeader.debug.log("start populateEmptyToolbar");
+    var hdrToolbar = document.getElementById("header-view-toolbar");
+    if (hdrToolbar) {
+      org.mozdev.compactHeader.debug.log("populateEmptyToolbar 1");
+      if (hdrToolbar.currentSet == "__empty") {
+        org.mozdev.compactHeader.debug.log("populateEmptyToolbar 2");
+        pub.CHTSetDefaultButtons();
+      }
+    }
+    org.mozdev.compactHeader.debug.log("stop populateEmptyToolbar");
+  };
+
 
   return pub;
 }();
diff --git a/test/compactheader/test-compactheader-toolbar.js b/test/compactheader/test-compactheader-toolbar.js
new file mode 100644
index 0000000..bf072af
--- /dev/null
+++ b/test/compactheader/test-compactheader-toolbar.js
@@ -0,0 +1,604 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird Mail Client.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blake Winton <bwinton at latte.ca>
+ *   Dan Mosedale <dmose at mozillamessaging.com>
+ *   Joachim Herb <Joachim.Herb at gmx.de>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Test that we can add a tag to a message without messing up the header.
+ */
+var MODULE_NAME = 'test-header-toolbar';
+
+var RELATIVE_ROOT = '../shared-modules';
+var MODULE_REQUIRES = ['folder-display-helpers', 'window-helpers',
+                       'address-book-helpers', 'mouse-event-helpers'];
+
+var elib = {};
+Cu.import('resource://mozmill/modules/elementslib.js', elib);
+var controller = {};
+Cu.import('resource://mozmill/modules/controller.js', controller);
+
+// The WindowHelper module
+var WindowHelper;
+
+var folder;
+
+const PREF = "toolbar.customization.usesheet";
+var prefBranch = Cc["@mozilla.org/preferences-service;1"]
+                    .getService(Ci.nsIPrefService).getBranch(null);
+
+function setupModule(module) {
+  let fdh = collector.getModule('folder-display-helpers');
+  fdh.installInto(module);
+  WindowHelper = collector.getModule('window-helpers');
+  WindowHelper.installInto(module);
+  let abh = collector.getModule('address-book-helpers');
+  abh.installInto(module);
+  let meh = collector.getModule('mouse-event-helpers');
+  meh.installInto(module);
+
+  folder = create_folder("MessageWindowB");
+
+  // create a message that has the interesting headers that commonly
+  // show up in the message header pane for testing
+  let msg = create_message({cc: msgGen.makeNamesAndAddresses(20), // YYY
+                            subject: "This is a really, really, really, really, really, really, really, really, long subject.",
+                            clobberHeaders: {
+                              "Newsgroups": "alt.test",
+                              "Reply-To": "J. Doe <j.doe at momo.invalid>",
+                              "Content-Base": "http://example.com/",
+                              "Bcc": "Richard Roe <richard.roe at momo.invalid>"
+                            }});
+
+  add_message_to_folder(folder, msg);
+
+  // create a message that has boring headers to be able to switch to and
+  // back from, to force the more button to collapse again.
+  msg = create_message();
+  add_message_to_folder(folder, msg);
+}
+
+
+/**
+ *  Make sure that opening the header toolbar customization dialog
+ *  does not break the get messages button in main toolbar
+ */
+//function test_get_msg_button_customize_header_toolbar(){
+//  select_message_in_folder(0);
+//
+//  // It is necessary to press the Get Message Button to get the popup menu populated
+//  mc.click(mc.aid("button-getmsg", {class: "toolbarbutton-menubutton-dropmarker"}));
+//  mc.ewait("button-getAllNewMsgSeparator");
+//
+//  var getMailButtonPopup = mc.eid("button-getMsgPopup").node;
+//  var originalServerCount = getMailButtonPopup.childElementCount;
+//
+//  // Open customization dialog, because it broke the Get Message Button popup menu
+//  // see https://bugzilla.mozilla.org/show_bug.cgi?id=565045
+//  let ctc = open_header_pane_toolbar_customization(mc);
+//  close_header_pane_toolbar_customization(ctc);
+//
+//  // Press the Get Message Button to populate popup menu again
+//  mc.click(mc.aid("button-getmsg", {class: "toolbarbutton-menubutton-dropmarker"}));
+//  mc.ewait("button-getAllNewMsgSeparator");
+//
+//  getMailButtonPopup = mc.eid("button-getMsgPopup").node;
+//  var finalServerCount = getMailButtonPopup.childElementCount;
+//
+//  if (originalServerCount != finalServerCount) {
+//    throw new Error("number of entries in Get Message Button popup menu after " +
+//                    "header toolbar customization " +
+//                    finalServerCount + " <> as before: " +
+//                    originalServerCount);
+//  }
+//}
+
+/**
+ *  Test header pane toolbar customization: Check for default button sets
+ */
+function test_customize_header_toolbar_check_default()
+{
+  let curMessage = select_message_in_folder(0);
+  let hdrToolbar = mc.eid("header-view-toolbar").node;
+  let hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  // In a fresh profile the currentset attribute does not
+  // exist, i.e. it returns empty. So check for both valid
+  // posiblities.
+  assert_true((hdrToolbar.getAttribute("currentset") == "") ||
+    (hdrToolbar.getAttribute("currentset") == hdrBarDefaultSet),
+    "Header Toolbar currentset should be empty or contain default buttons "+
+    "but contains: " + hdrToolbar.getAttribute("currentset"));
+  // Now make sure, that also the attribute gets set:
+  restore_and_check_default_buttons(mc);
+
+  // Display message in new window and check that the default
+  // buttons are shown there.
+  let msgc = open_selected_message_in_new_window();
+  assert_selected_and_displayed(msgc, curMessage);
+  let hdrToolbar = msgc.eid("header-view-toolbar").node;
+  let hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  // In a fresh profile the currentset attribute does not
+  // exist, i.e. it returns empty. So check for both valid
+  // posiblities.
+  assert_true((hdrToolbar.getAttribute("currentset") == "") ||
+    (hdrToolbar.getAttribute("currentset") == hdrBarDefaultSet),
+    "Header Toolbar currentset should be empty or contain default buttons "+
+    "but contains: " + hdrToolbar.getAttribute("currentset"));
+  // Now make sure, that also the attribute gets set:
+  restore_and_check_default_buttons(msgc);
+
+  close_window(msgc);
+}
+
+///**
+// *  Test header pane toolbar customization: Reorder buttons
+// */
+function test_customize_header_toolbar_reorder_buttons()
+{
+  let curMessage = select_message_in_folder(0);
+
+  // Restore the default buttons to get defined starting conditions.
+  restore_and_check_default_buttons(mc);
+
+  // Save the currentSet of the toolbar before opening the
+  // customization dialog, to get out of the way of the
+  // wrapper- prefix.
+  let toolbar = mc.eid("header-view-toolbar").node;
+  let oldSet = filterInvisibleButtons(mc, toolbar.currentSet).split(",");
+
+  let ctc = open_header_pane_toolbar_customization(mc);
+  let currentSet = filterInvisibleButtons(mc, toolbar.currentSet).split(",");
+
+  for (let i=1; i<currentSet.length; i++) {
+    let button1 = mc.e(currentSet[i]);
+    let button2 = mc.e(currentSet[i-1]);
+    // Move each button to the left of its left neighbour starting with
+    // the second button, i.e. reverse the order of the buttons.
+    drag_n_drop_element(button1, mc.window, button2, mc.window, 0.25, 0.0, toolbar);
+  }
+  close_header_pane_toolbar_customization(ctc);
+
+  // Check, if the toolbar is really in reverse order of beginning.
+  let reverseSet = oldSet.reverse().join(",");
+  assert_equals(filterInvisibleButtons(mc, toolbar.currentSet), reverseSet);
+  assert_equals(filterInvisibleButtons(mc, toolbar.getAttribute("currentset")),
+      reverseSet);
+
+  // Display message in new window and check that the default
+  // buttons are shown there.
+  let msgc = open_selected_message_in_new_window();
+  assert_selected_and_displayed(msgc, curMessage);
+  let hdrToolbar = msgc.eid("header-view-toolbar").node;
+  let hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  assert_equals(hdrToolbar.getAttribute("currentset"), hdrBarDefaultSet);
+  close_window(msgc);
+
+  // Leave the toolbar in the default state.
+  restore_and_check_default_buttons(mc);
+}
+//
+///**
+// *  Test header pane toolbar customization: Change buttons in
+// *  separate mail window
+// */
+function test_customize_header_toolbar_separate_window()
+{
+  let curMessage = select_message_in_folder(0);
+
+  // Restore the default buttons to get defined starting conditions.
+  restore_and_check_default_buttons(mc);
+
+  // Display message in new window and check that the default
+  // buttons are shown there.
+  let msgc = open_selected_message_in_new_window();
+  assert_selected_and_displayed(msgc, curMessage);
+  let hdrToolbar = msgc.eid("header-view-toolbar").node;
+  let hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  assert_equals(hdrToolbar.getAttribute("currentset"), hdrBarDefaultSet);
+
+  // Save the currentSet of the toolbar before opening the
+  // customization dialog, to get out of the way of the
+  // wrapper- prefix.
+  let toolbar = msgc.eid("header-view-toolbar").node;
+  let oldSet = filterInvisibleButtons(msgc, toolbar.currentSet).split(",");
+
+  let ctc = open_header_pane_toolbar_customization(msgc);
+  let currentSet = filterInvisibleButtons(msgc, toolbar.currentSet).split(",");
+  for (let i=1; i<currentSet.length; i++) {
+    let button1 = msgc.e(currentSet[i]);
+    let button2 = msgc.e(currentSet[i-1]);
+    // Move each button to the left of its left neighbour starting with
+    // the second button, i.e. reverse the order of the buttons.
+    drag_n_drop_element(button1, msgc.window, button2, msgc.window, 0.25, 0.0, toolbar);
+  }
+  close_header_pane_toolbar_customization(ctc);
+
+  // Check, if the toolbar is really in reverse order of beginning.
+  let reverseSet = oldSet.reverse().join(",");
+  assert_equals(filterInvisibleButtons(msgc, toolbar.currentSet), reverseSet);
+  assert_equals(filterInvisibleButtons(msgc, toolbar.getAttribute("currentset")),
+      reverseSet);
+
+  // Make sure we have a different window open, so that we don't start shutting
+  // down just because the last window was closed
+  let abwc = openAddressBook();
+  // The 3pane window is closed and opened again.
+  close3PaneWindow();
+  close_window(msgc);
+
+  mc = open3PaneWindow();
+  abwc.window.close();
+  select_message_in_folder(0);
+
+  // Check, if the buttons in the mail3pane window are the correct ones.
+  let hdrToolbar = mc.eid("header-view-toolbar").node;
+  let hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  assert_equals(hdrToolbar.getAttribute("currentset"), hdrBarDefaultSet);
+
+  // Open separate mail window again and check another time.
+  let msgc = open_selected_message_in_new_window();
+  assert_selected_and_displayed(msgc, curMessage);
+  let toolbar = msgc.eid("header-view-toolbar").node;
+  assert_equals(filterInvisibleButtons(msgc, toolbar.currentSet), reverseSet);
+  assert_equals(filterInvisibleButtons(msgc, toolbar.getAttribute("currentset")),
+      reverseSet);
+
+  // Leave the toolbar in the default state.
+  restore_and_check_default_buttons(msgc);
+  close_window(msgc);
+}
+
+/**
+ *  Test header pane toolbar customization: Remove buttons
+ */
+function test_customize_header_toolbar_remove_buttons(){
+  // Save currentset of toolbar for adding the buttons back
+  // at the end.
+  var lCurrentset;
+
+  select_message_in_folder(0);
+
+  // Restore the default buttons to get defined starting conditions.
+  restore_and_check_default_buttons(mc);
+
+  let ctc = open_header_pane_toolbar_customization(mc);
+  let toolbar = mc.eid("header-view-toolbar").node;
+  lCurrentset = filterInvisibleButtons(mc, toolbar.currentSet).split(",");
+  let target = ctc.e("palette-box");
+  for (let i=0; i<lCurrentset.length; i++) {
+    let button = mc.e(lCurrentset[i]);
+    drag_n_drop_element(button, mc.window, target, ctc.window, 0.5, 0.5, toolbar);
+  }
+  close_header_pane_toolbar_customization(ctc);
+
+  // Check, if the toolbar is really empty.
+  let toolbar = mc.eid("header-view-toolbar").node;
+  assert_equals(filterInvisibleButtons(mc, toolbar.currentSet), "__empty");
+  assert_equals(filterInvisibleButtons(mc, toolbar.getAttribute("currentset")),
+      "__empty");
+
+  // Move to the next message and check again.
+  let curMessage = select_message_in_folder(1);
+  assert_equals(filterInvisibleButtons(mc, toolbar.currentSet), "__empty");
+  assert_equals(filterInvisibleButtons(mc, toolbar.getAttribute("currentset")),
+      "__empty");
+
+  // Display message in new window and check that the default
+  // buttons are shown there.
+  let msgc = open_selected_message_in_new_window();
+  assert_selected_and_displayed(msgc, curMessage);
+  let hdrToolbar = msgc.eid("header-view-toolbar").node;
+  let hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  assert_equals(hdrToolbar.getAttribute("currentset"), hdrBarDefaultSet);
+  close_window(msgc);
+
+  // Check the persistency of the buttons.
+
+  // Make sure we have a different window open, so that we don't start shutting
+  // down just because the last window was closed
+  let abwc = openAddressBook();
+  // The 3pane window is closed.
+  close3PaneWindow();
+  mc = open3PaneWindow();
+  abwc.window.close();
+  select_message_in_folder(0);
+
+  let toolbar = mc.eid("header-view-toolbar").node;
+  assert_equals(filterInvisibleButtons(mc, toolbar.currentSet), "__empty");
+  assert_equals(filterInvisibleButtons(mc, toolbar.getAttribute("currentset")),
+      "__empty");
+
+  // Check that all removed buttons show up in the palette
+  // and move it back in the toolbar.
+  let ctc = open_header_pane_toolbar_customization(mc);
+  let toolbar = mc.eid("header-view-toolbar").node;
+  let palette = ctc.e("palette-box");
+  for (let i=0; i<lCurrentset.length; i++) {
+    let button = ctc.e(lCurrentset[i]);
+    assert_true(button!=null, "Button " + lCurrentset[i] + " not in palette");
+    // Drop each button to the right end of the toolbar, so we should get the
+    // original order.
+    drag_n_drop_element(button, ctc.window, toolbar, mc.window, 0.99, 0.5, palette);
+  }
+  close_header_pane_toolbar_customization(ctc);
+
+  let toolbar = mc.eid("header-view-toolbar").node;
+  assert_equals(filterInvisibleButtons(mc, toolbar.currentSet),
+      filterInvisibleButtons(mc, hdrBarDefaultSet));
+  assert_equals(filterInvisibleButtons(mc, toolbar.getAttribute("currentset")),
+      filterInvisibleButtons(mc, hdrBarDefaultSet));
+}
+
+/**
+ *  Test header pane toolbar customization dialog layout
+ */
+function test_customize_header_toolbar_dialog_style(){
+  select_message_in_folder(0);
+
+  // Restore the default buttons to get defined starting conditions.
+  restore_and_check_default_buttons(mc);
+
+  let ctc = open_header_pane_toolbar_customization(mc);
+
+  // The full mode menulist entry is hidden, because in the header toolbar
+  // this mode is disabled.
+  let fullMode = ctc.window.document.getElementById("main-box").
+    querySelector("[value='full']");
+  assert_equals(ctc.window.getComputedStyle(fullMode).getPropertyValue("display"), "none");
+  // The icon menulist entry is selected, because in the header toolbar with CompactHeader installed
+  // this is the default mode.
+  let iconMode = ctc.window.document.getElementById("modelist").
+    querySelector("[value='icons']");
+  assert_equals(iconMode.getAttribute("selected"), "true");
+
+  // The small icons checkbox is hidden, because in the header toolbar
+  // this mode is the only possible (therefore, the checked attribute is true).
+  let smallIcons = ctc.eid("smallicons").node;
+  assert_equals(smallIcons.getAttribute("checked"), "true");
+  assert_equals(ctc.window.getComputedStyle(smallIcons).getPropertyValue("display"), "none");
+
+  // The add new toolbar button is hidden, because in the header toolbar
+  // this functionality is not available.
+  let addNewToolbar = ctc.window.document.getElementById("main-box").
+    querySelector("[oncommand='addNewToolbar();']");
+  assert_equals(ctc.window.getComputedStyle(addNewToolbar).getPropertyValue("display"), "none");
+
+  close_header_pane_toolbar_customization(ctc);
+}
+
+/**
+ *  Test header pane toolbar customization dialog for button style changes
+ */
+function test_customize_header_toolbar_change_button_style(){
+  select_message_in_folder(0);
+
+  // Restore the default buttons to get defined starting conditions.
+  restore_and_check_default_buttons(mc);
+  // The default mode is icon visible only.
+  subtest_buttons_style("-moz-box", "none");
+
+  // Change the button style to text and icon mode
+  let ctc = open_header_pane_toolbar_customization(mc);
+  let iconMode = ctc.window.document.getElementById("main-box").
+    querySelector("[value='textbesideicon']");
+  ctc.click(new elib.Elem(iconMode));
+  close_header_pane_toolbar_customization(ctc);
+
+  subtest_buttons_style("-moz-box", "-moz-box");
+
+  // Change the button style to icon mode only
+  let ctc = open_header_pane_toolbar_customization(mc);
+  let iconMode = ctc.window.document.getElementById("main-box").
+    querySelector("[value='icons']");
+  ctc.click(new elib.Elem(iconMode));
+  close_header_pane_toolbar_customization(ctc);
+
+  subtest_buttons_style("-moz-box", "none");
+
+  // Change the button style to text (only) mode
+  let ctc = open_header_pane_toolbar_customization(mc);
+  let textMode = ctc.window.document.getElementById("main-box").
+    querySelector("[value='text']");
+  ctc.click(new elib.Elem(textMode));
+  close_header_pane_toolbar_customization(ctc);
+
+  subtest_buttons_style("none", "-moz-box");
+
+  // The default mode is icon visible only.
+  restore_and_check_default_buttons(mc);
+  subtest_buttons_style("-moz-box", "none");
+}
+
+/**
+ * Select message in current (global) folder.
+ */
+function select_message_in_folder(aMessageNum)
+{
+  be_in_folder(folder);
+
+  // select and open the first message
+  let curMessage = select_click_row(aMessageNum);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  return curMessage;
+}
+
+/**
+ *  Check all buttons in the toolbar for the correct style
+ *  of text and icon.
+ */
+function subtest_buttons_style(aIconVisibility, aLabelVisibility)
+{
+  let toolbar = mc.eid("header-view-toolbar").node;
+  let currentSet = filterInvisibleButtons(mc, toolbar.currentSet).split(",");
+
+  for (let i=0; i<currentSet.length; i++) {
+    // XXX For the moment only consider normal toolbar buttons.
+    // XXX Handling of toolbaritem buttons has to be added later,
+    // XXX especially the smart reply button!
+    if (mc.eid(currentSet[i]).node.tagName == "toolbarbutton") {
+      let icon = mc.aid(currentSet[i], {class: "toolbarbutton-icon"}).node;
+      let label = mc.aid(currentSet[i], {class: "toolbarbutton-text"}).node;
+      assert_equals(mc.window.getComputedStyle(icon).getPropertyValue("display"), aIconVisibility);
+      assert_equals(mc.window.getComputedStyle(label).getPropertyValue("display"), aLabelVisibility);
+    }
+  }
+}
+
+/**
+ *  Restore the default buttons in the header pane toolbar
+ *  by clicking the corresponding button in the palette dialog
+ *  and check if it worked.
+ */
+function restore_and_check_default_buttons(aController)
+{
+  let ctc = open_header_pane_toolbar_customization(aController);
+  let restoreButton = ctc.window.document.getElementById("main-box").
+    querySelector("[oncommand='overlayRestoreDefaultSet();']");
+  ctc.click(new elib.Elem(restoreButton));
+  close_header_pane_toolbar_customization(ctc);
+
+  let hdrToolbar = aController.eid("header-view-toolbar").node;
+  let hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  assert_equals(hdrToolbar.getAttribute("currentset"), hdrBarDefaultSet);
+}
+
+/*
+ * Open the header pane toolbar customization dialog.
+ */
+function open_header_pane_toolbar_customization(aController)
+{
+  let ctc;
+  aController.click(aController.eid("CustomizeHeaderToolbar"));
+  // Depending on preferences the customization dialog is
+  // either a normal window or embedded into a sheet.
+  if (prefBranch.getBoolPref(PREF, true)) {
+    aController.ewait("donebutton");
+    let contentWindow = aController.eid("customizeToolbarSheetIFrame").node.contentWindow;
+    ctc = WindowHelper.augment_controller(new controller.MozMillController(contentWindow));
+  }
+  else {
+    ctc = WindowHelper.wait_for_existing_window("mail:customizetoolbar");
+  }
+  return ctc;
+}
+
+/*
+ * Close the header pane toolbar customization dialog.
+ */
+function close_header_pane_toolbar_customization(aCtc)
+{
+  aCtc.click(aCtc.eid("donebutton"));
+  // XXX There should be an equivalent for testing the closure of
+  // XXX the dialog embedded in a sheet, but I do not know how.
+  if (!prefBranch.getBoolPref(PREF, true)) {
+   assert_true(aCtc.window.closed, "The customization dialog is not closed.");
+  }
+}
+
+/**
+ *  Helper function to open an extra window, so that the 3pane
+ *  window can be closed and opend again for persistancy checks.
+ *  They are copied from the test-session-store.js.
+ */
+function close3PaneWindow() {
+  let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
+    getService(Ci.nsIWindowMediator);
+  let mail3PaneWindow = windowMediator.getMostRecentWindow("mail:3pane");
+  // close the 3pane window
+  mail3PaneWindow.close();
+}
+
+function open3PaneWindow() {
+  let windowWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+    getService(Ci.nsIWindowWatcher);
+  WindowHelper.plan_for_new_window("mail:3pane");
+  windowWatcher.openWindow(null,
+                           "chrome://messenger/content/messenger.xul", "",
+                           "all,chrome,dialog=no,status,toolbar",
+                           null);
+  return WindowHelper.wait_for_new_window("mail:3pane");
+}
+
+function openAddressBook() {
+  let windowWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+    getService(Ci.nsIWindowWatcher);
+  WindowHelper.plan_for_new_window("mail:addressbook");
+  windowWatcher.openWindow(
+                      null,
+                      "chrome://messenger/content/addressbook/addressbook.xul", "",
+                      "all,chrome,dialog=no,status,toolbar",
+                      null);
+  return WindowHelper.wait_for_new_window("mail:addressbook");
+}
+
+/*
+ * Remove invsible buttons from (comma separated) buttons list
+ */
+function filterInvisibleButtons(aController, aButtons) {
+  let buttons = aButtons.split(",");
+  let result = new Array;
+
+  for (let i=1; i<buttons.length; i++) {
+    button = buttons[i].replace(new RegExp("wrapper-"), "");
+    if ((aController.eid(button).node) &&
+        (!aController.eid(button).node.getAttribute("collapsed"))
+        ) {
+      result.push(buttons[i]);
+    }
+  }
+
+  let strResult;
+  if (result.length > 0) {
+    strResult = result.join(",");
+  }
+  else {
+    strResult = "__empty";
+  }
+
+  return strResult;
+}
diff --git a/test/compactheader/test-message-header.js b/test/compactheader/test-message-header.js
new file mode 100644
index 0000000..94950c3
--- /dev/null
+++ b/test/compactheader/test-message-header.js
@@ -0,0 +1,837 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird Mail Client.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blake Winton <bwinton at latte.ca>
+ *   Dan Mosedale <dmose at mozillamessaging.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Test that we can add a tag to a message without messing up the header.
+ */
+var MODULE_NAME = 'test-message-header';
+
+var RELATIVE_ROOT = '../shared-modules';
+var MODULE_REQUIRES = ['folder-display-helpers', 'window-helpers',
+                       'address-book-helpers'];
+
+var elib = {};
+Cu.import('resource://mozmill/modules/elementslib.js', elib);
+
+var folder;
+
+function setupModule(module) {
+  let fdh = collector.getModule('folder-display-helpers');
+  fdh.installInto(module);
+  let wh = collector.getModule('window-helpers');
+  wh.installInto(module);
+  let abh = collector.getModule('address-book-helpers');
+  abh.installInto(module);
+
+  folder = create_folder("MessageWindowA");
+
+  // create a message that has the interesting headers that commonly
+  // show up in the message header pane for testing
+  let msg = create_message({cc: msgGen.makeNamesAndAddresses(20), // YYY
+                            subject: "This is a really, really, really, really, really, really, really, really, long subject.",
+                            clobberHeaders: {
+                              "Newsgroups": "alt.test",
+                              "Reply-To": "J. Doe <j.doe at momo.invalid>",
+                              "Content-Base": "http://example.com/",
+                              "Bcc": "Richard Roe <richard.roe at momo.invalid>"
+                            }});
+
+  add_message_to_folder(folder, msg);
+
+  // create a message that has boring headers to be able to switch to and
+  // back from, to force the more button to collapse again.
+  msg = create_message();
+  add_message_to_folder(folder, msg);
+}
+
+function test_add_tag_with_really_long_label() {
+  be_in_folder(folder);
+
+  // select the first message, which will display it
+  let curMessage = select_click_row(0);
+
+  assert_selected_and_displayed(mc, curMessage);
+
+  let topColumn = mc.eid("expandedHeadersNameColumn").node;
+  let bottomColumn = mc.eid("expandedHeaders2NameColumn").node;
+
+  if (topColumn.clientWidth != bottomColumn.clientWidth)
+    throw new Error("Header columns have different widths!  " +
+                    topColumn.clientWidth + " != " + bottomColumn.clientWidth);
+  let defaultWidth = topColumn.clientWidth;
+
+  // Make the tags label really long.
+  let tagsLabel = mc.eid("expandedtagsLabel").node;
+  let oldTagsValue = tagsLabel.value;
+  tagsLabel.value = "taaaaaaaaaaaaaaaaaags";
+
+  if (topColumn.clientWidth != bottomColumn.clientWidth) {
+    tagsLabel.value = oldTagsValue;
+    throw new Error("Header columns have different widths!  " +
+                    topColumn.clientWidth + " != " + bottomColumn.clientWidth);
+  }
+  if (topColumn.clientWidth != defaultWidth) {
+    tagsLabel.value = oldTagsValue;
+    throw new Error("Header columns changed width!  " +
+                    topColumn.clientWidth + " != " + defaultWidth);
+  }
+
+  // Add the first tag, and make sure that the label are the same length.
+  mc.keypress(mc.eid("expandedHeadersNameColumn"), "1", {});
+
+  if (topColumn.clientWidth != bottomColumn.clientWidth) {
+    tagsLabel.value = oldTagsValue;
+    throw new Error("Header columns have different widths!  " +
+                    topColumn.clientWidth + " != " + bottomColumn.clientWidth);
+  }
+  if (topColumn.clientWidth == defaultWidth) {
+    tagsLabel.value = oldTagsValue;
+    throw new Error("Header columns didn't change width!  " +
+                    topColumn.clientWidth + " == " + defaultWidth);
+  }
+
+  // Remove the tag and put it back so that the a11y label gets regenerated
+  // with the normal value rather than "taaaaaaaags"
+  tagsLabel.value = oldTagsValue;
+  mc.keypress(mc.eid("expandedHeadersNameColumn"), "1", {});
+  mc.keypress(mc.eid("expandedHeadersNameColumn"), "1", {});
+}
+
+/**
+ * @param headerName used for pretty-printing in exceptions
+ * @param headerValueElement code to be eval()ed returning the DOM element
+ *        with the data.
+ * @param expectedName code to be eval()ed returning the expected value of
+ *                     nsIAccessible.name for the DOM element in question
+ * @param expectedRole the expected value for nsIAccessible.role
+ */
+let headersToTest = [
+{
+  headerName: "Subject",
+  headerValueElement: "mc.a('expandedsubjectBox', {class: 'headerValue'})",
+  expectedName: "mc.e('expandedsubjectLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.textContent",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "Content-Base",
+  headerValueElement: "mc.a('expandedcontent-baseBox', {class: 'headerValue text-link headerValueUrl'})",
+  expectedName: "mc.e('expandedcontent-baseLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.textContent",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "From",
+  headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" +
+                      "mc.a('expandedfromBox', {tagName: 'mail-emailaddress'})," +
+                      "'class', 'emailDisplayButton')",
+  expectedName: "mc.e('expandedfromLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.parentNode.getAttribute('fullAddress')",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "To",
+  headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" +
+                      "mc.a('expandedtoBox', {tagName: 'mail-emailaddress'})," +
+                      "'class', 'emailDisplayButton')",
+  expectedName: "mc.e('expandedtoLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.parentNode.getAttribute('fullAddress')",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "Cc",
+  headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" +
+                      "mc.a('expandedccBox', {tagName: 'mail-emailaddress'})," +
+                      "'class', 'emailDisplayButton')",
+  expectedName: "mc.e('expandedccLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.parentNode.getAttribute('fullAddress')",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "Bcc",
+  headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" +
+                      "mc.a('expandedbccBox', {tagName: 'mail-emailaddress'})," +
+                      "'class', 'emailDisplayButton')",
+  expectedName: "mc.e('expandedbccLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.parentNode.getAttribute('fullAddress')",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "Reply-To",
+  headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" +
+                      "mc.a('expandedreply-toBox', {tagName: 'mail-emailaddress'})," +
+                      "'class', 'emailDisplayButton')",
+  expectedName: "mc.e('expandedreply-toLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.parentNode.getAttribute('fullAddress')",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "Newsgroups",
+  headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" +
+                      "mc.a('expandednewsgroupsBox', {tagName: 'mail-newsgroup'})," +
+                      "'class', 'newsgrouplabel')",
+  expectedName: "mc.e('expandednewsgroupsLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.parentNode.parentNode.getAttribute('newsgroup')",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY
+},
+{
+  headerName: "Tags",
+  headerValueElement: "mc.a('expandedtagsBox', {class: 'tagvalue blc-FF0000'})",
+  expectedName: "mc.e('expandedtagsLabel').value.slice(0,-1) + ': ' + " +
+                "headerValueElement.getAttribute('value')",
+  expectedRole: Ci.nsIAccessibleRole.ROLE_LABEL
+}
+];
+
+// used to get the accessible object for a DOM node
+let gAccRetrieval = Cc["@mozilla.org/accessibleRetrieval;1"].
+                    getService(Ci.nsIAccessibleRetrieval);
+
+/**
+ * Use the information from aHeaderInfo to verify that screenreaders will
+ * do the right thing with the given message header.
+ *
+ * @param {Object} aHeaderInfo  Information about how to do the verification;
+ *                              See the comments above the headersToTest array
+ *                              for details.
+ */
+function verify_header_a11y(aHeaderInfo) {
+  // XXX Don't use eval here.
+  let headerValueElement = eval(aHeaderInfo.headerValueElement);
+
+  let headerAccessible = gAccRetrieval.getAccessibleFor(headerValueElement);
+  if (headerAccessible.role != aHeaderInfo.expectedRole) {
+    throw new Error("role for " + aHeaderInfo.headerName + " was " +
+                    headerAccessible.role + "; should have been " +
+                    aHeaderInfo.expectedRole);
+  }
+
+  // XXX Don't use eval here.
+  let expectedName = eval(aHeaderInfo.expectedName);
+  if (headerAccessible.name != expectedName) {
+    throw new Error("headerAccessible.name for " + aHeaderInfo.headerName +
+                    " was '" + headerAccessible.name + "'; expected '" +
+                    expectedName + "'");
+  }
+}
+
+/**
+ * Test the accessibility attributes of the various message headers.
+ *
+ * XXX This test used to be after test_more_button_with_many_recipients,
+ * however, there were some accessibility changes that it didn't seem to play
+ * nicely with, and the toggling of the "more" button on the cc field was
+ * causing this test to fail on the cc element. Tests with accessibilty
+ * hardware/software showed that the code was working fine. Therefore the test
+ * may be suspect.
+ */
+function test_a11y_attrs() {
+  // skip this test on platforms that don't support accessibility
+  if (!("nsIAccessibleRole" in Components.interfaces))
+    return;
+
+  be_in_folder(folder);
+
+  // select and open the first message
+  let curMessage = select_click_row(0);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  headersToTest.forEach(verify_header_a11y);
+}
+
+function test_more_button_with_many_recipients()
+{
+  // Start on the interesting message.
+  let curMessage = select_click_row(0);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  // Check the mode of the header.
+  let headerBox = mc.eid("expandedHeaderView");
+  let previousHeaderMode = headerBox.node.getAttribute("show_header_mode");
+
+  // Click the "more" button.
+  let moreIndicator = mc.eid("expandedccBox");
+  moreIndicator = mc.window.document.getAnonymousElementByAttribute(
+                    moreIndicator.node, "anonid", "more");
+  moreIndicator = new elementslib.Elem(moreIndicator);
+  mc.click(moreIndicator);
+
+  // Check the new mode of the header.
+  if (headerBox.node.getAttribute("show_header_mode") != "all")
+    throw new Error("Header Mode didn't change to 'all'!  " + "old=" +
+                    previousHeaderMode + ", new=" +
+                    headerBox.node.getAttribute("show_header_mode"));
+
+  // Switch to the boring message, to force the more button to collapse.
+  curMessage = select_click_row(1);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  // Check the even newer mode of the header.
+  if (headerBox.node.getAttribute("show_header_mode") != previousHeaderMode)
+    throw new Error("Header Mode changed from " + previousHeaderMode +
+                    " to " + headerBox.node.getAttribute("show_header_mode") +
+                    " and didn't change back.");
+}
+
+/**
+ * Test that we can open up the inline contact editor when we
+ * click on the star.
+ */
+function test_clicking_star_opens_inline_contact_editor()
+{
+  // Make sure we're in the right folder
+  be_in_folder(folder);
+
+  // Add a new message
+  let msg = create_message();
+  add_message_to_folder(folder, msg);
+
+  // Open the latest message
+  let curMessage = select_click_row(-1);
+  // Make sure the star is clicked, and we add the
+  // new contact to our address book
+  let toDescription = mc.a('expandedtoBox', {class: "headerValue"});
+
+  // Ensure that the inline contact editing panel is not open
+  let contactPanel = mc.eid('editContactPanel').getNode();
+  assert_not_equals(contactPanel.state, "open");
+  subtest_more_widget_star_click(toDescription);
+
+  // Ok, if we're here, then the star has been clicked, and
+  // the contact has been added to our AB.
+  let addrs = toDescription.getElementsByTagName('mail-emailaddress');
+  let lastAddr = addrs[addrs.length-1];
+
+  // Click on the star, and ensure that the inline contact
+  // editing panel opens
+  mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
+  assert_equals(contactPanel.state, "open");
+  contactPanel.hidePopup();
+}
+
+/**
+ * Test that if a contact belongs to a mailing list within their
+ * address book, then the inline contact editor will not allow
+ * the user to change what address book the contact belongs to.
+ * The editor should also show a message to explain why the
+ * contact cannot be moved.
+ */
+function test_address_book_switch_disabled_on_contact_in_mailing_list()
+{
+  const MAILING_LIST_DIRNAME = "Some Mailing List";
+  const ADDRESS_BOOK_NAME = "Some Address Book";
+  // Add a new message
+  let msg = create_message();
+  add_message_to_folder(folder, msg);
+
+  // Make sure we're in the right folder
+  be_in_folder(folder);
+
+  // Open the latest message
+  let curMessage = select_click_row(-1);
+
+  // Make sure the star is clicked, and we add the
+  // new contact to our address book
+  let toDescription = mc.a('expandedtoBox', {class: "headerValue"});
+
+  // Ensure that the inline contact editing panel is not open
+  let contactPanel = mc.eid('editContactPanel').getNode();
+  assert_not_equals(contactPanel.state, "open");
+
+  subtest_more_widget_star_click(toDescription);
+
+  // Ok, if we're here, then the star has been clicked, and
+  // the contact has been added to our AB.
+  let addrs = toDescription.getElementsByTagName('mail-emailaddress');
+  let lastAddr = addrs[addrs.length-1];
+
+  // Click on the star, and ensure that the inline contact
+  // editing panel opens
+  mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
+  assert_equals(contactPanel.state, "open");
+
+  let abDrop = mc.eid('editContactAddressBookList').getNode();
+  let warningMsg = mc.eid('contactMoveDisabledText').getNode();
+
+  // Ensure that the address book dropdown is not disabled
+  assert_true(!abDrop.disabled);
+  // We should not be displaying any warning
+  assert_true(warningMsg.collapsed);
+
+  // Now close the popup
+  contactPanel.hidePopup();
+
+  // For the contact that was added, create a mailing list in the
+  // address book it resides in, and then add that contact to the
+  // mailing list
+  addrs = toDescription.getElementsByTagName('mail-emailaddress');
+  let targetAddr = addrs[addrs.length-1].getAttribute("emailAddress");
+
+  let cards = get_cards_in_all_address_books_for_email(targetAddr);
+
+  // There should be only one copy of this email address
+  // in the address books.
+  assert_equals(cards.length, 1);
+  let card = cards[0];
+
+  // Remove the card from any of the address books
+  ensure_no_card_exists(targetAddr);
+
+  // Add the card to a new address book, and insert it
+  // into a mailing list under that address book
+  let ab = create_mork_address_book(ADDRESS_BOOK_NAME);
+  ab.dropCard(card, false);
+  let ml = create_mailing_list(MAILING_LIST_DIRNAME);
+  ab.addMailList(ml);
+
+  // Now we have to retrieve the mailing list from
+  // the address book, in order for us to add and
+  // delete cards from it.
+  ml = get_mailing_list_from_address_book(ab, MAILING_LIST_DIRNAME);
+
+  ml.addressLists.appendElement(card, false);
+
+  // Re-open the inline contact editing panel
+  mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
+  assert_equals(contactPanel.state, "open");
+
+  // The dropdown should be disabled now
+  assert_true(abDrop.disabled);
+  // We should be displaying a warning
+  assert_true(!warningMsg.collapsed);
+
+  contactPanel.hidePopup();
+
+  // And if we remove the contact from the mailing list, the
+  // warning should be gone and the address book switching
+  // menu re-enabled.
+
+  let cardArray = Cc["@mozilla.org/array;1"]
+                  .createInstance(Ci.nsIMutableArray);
+  cardArray.appendElement(card, false);
+  ml.deleteCards(cardArray);
+
+  // Re-open the inline contact editing panel
+  mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
+  assert_equals(contactPanel.state, "open");
+
+  // Ensure that the address book dropdown is not disabled
+  assert_true(!abDrop.disabled);
+  // We should not be displaying any warning
+  assert_true(warningMsg.collapsed);
+
+  contactPanel.hidePopup();
+}
+
+/**
+ * Test that clicking the adding an address node adds it to the address book.
+ */
+function test_add_contact_from_context_menu() {
+  // Click the contact to show the emailAddressPopup popup menu.
+  mc.click(mc.aid("expandedfromBox", {tagName: "mail-emailaddress"}));
+
+  var addToAddressBookItem = mc.window.document.getElementById("addToAddressBookItem");
+  if (addToAddressBookItem.hidden)
+    throw new Error("addToAddressBookItem is hidden for unknown contact");
+  var editContactItem = mc.window.document.getElementById("editContactItem");
+  if (!editContactItem.getAttribute("hidden"))
+    throw new Error("editContactItem is NOT hidden for unknown contact");
+
+  // Click the Add to Address Book context menu entry.
+  mc.click(mc.eid("addToAddressBookItem"));
+  // (for reasons unknown, the pop-up does not close itself)
+  close_popup(mc, mc.eid("emailAddressPopup"));
+
+  // Now click the contact again, the context menu should now show the
+  // Edit Contact menu instead.
+  mc.click(mc.aid("expandedfromBox", {tagName: "mail-emailaddress"}));
+  // (for reasons unknown, the pop-up does not close itself)
+  close_popup(mc, mc.eid("emailAddressPopup"));
+
+  addToAddressBookItem = mc.window.document.getElementById("addToAddressBookItem");
+  if (!addToAddressBookItem.hidden)
+    throw new Error("addToAddressBookItem is NOT hidden for known contact");
+  editContactItem = mc.window.document.getElementById("editContactItem");
+  if (editContactItem.hidden)
+    throw new Error("editContactItem is hidden for known contact");
+}
+
+function test_that_msg_without_date_clears_previous_headers() {
+  be_in_folder(folder);
+
+  // create a message
+  let msg = create_message();
+
+  // ensure that this message doesn't have a Date header
+  delete msg.headers.Date;
+
+  // this will add the message to the end of the folder
+  add_message_to_folder(folder, msg);
+
+  // select and open the first message
+  let curMessage = select_click_row(0);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  // Since we didn't give create_message an argument that would create a
+  // Newsgroups header, the newsgroups <row> element should be collapsed.
+  // However, since the previously displayed message _did_ have such a header,
+  // certain bugs in the display of this header could cause the collapse
+  // never to have happened.
+  if (mc.e("expandednewsgroupsRow").collapsed != true) {
+    throw new Error("Expected <row> elemnent for Newsgroups header to be " +
+                    "collapsed, but it wasn't\n!");
+  }
+}
+
+/**
+ * Test various aspects of the (n more) widgetry.
+ */
+function test_more_widget() {
+  // generate message with 35 recips (effectively guarantees overflow for n=3)
+  be_in_folder(folder);
+  let msg = create_message({toCount: 35});
+
+  // add the message to the end of the folder
+  add_message_to_folder(folder, msg);
+
+  // select and open the last message
+  let curMessage = select_click_row(-1);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  // get the description element containing the addresses
+  let toDescription = mc.a('expandedtoBox', {class: "headerValue"});
+
+  subtest_more_widget_display(toDescription);
+  subtest_more_widget_click(toDescription);
+  subtest_more_widget_star_click(toDescription);
+}
+
+/**
+ * Test that all addresses are shown in show all header mode
+ */
+function test_show_all_header_mode() {
+  // generate message with 35 recips (effectively guarantees overflow for n=3)
+  be_in_folder(folder);
+  let msg = create_message({toCount: 35});
+
+  // add the message to the end of the folder
+  add_message_to_folder(folder, msg);
+
+  // select and open the last message
+  let curMessage = select_click_row(-1);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  // get the description element containing the addresses
+  let toDescription = mc.a('expandedtoBox', {class: "headerValue"});
+
+  change_to_header_normal_mode();
+  subtest_more_widget_display(toDescription);
+  subtest_change_to_all_header_mode(toDescription);
+  change_to_header_normal_mode();
+  subtest_more_widget_click(toDescription);
+}
+
+function change_to_header_normal_mode() {
+  // XXX Clicking on check menu items doesn't work in 1.4.1b1 (bug 474486)...
+  //  mc.click(new elib.Elem(mc.menus.View.viewheadersmenu.viewnormalheaders));
+  // ... so call the function instead.
+  mc.window.MsgViewNormalHeaders();
+  mc.sleep(0);
+}
+
+function change_to_all_header_mode() {
+  // XXX Clicking on check menu items doesn't work in 1.4.1b1 (bug 474486)...
+  //  mc.click(new elib.Elem(mc.menus.View.viewheadersmenu.viewallheaders));
+  // ... so call the function instead.
+  mc.window.MsgViewAllHeaders();
+  mc.sleep(0);
+}
+
+/**
+ * Get the number of lines in one of the multi-address fields
+ * @param node the description element containing the addresses
+ * @return the number of lines
+ */
+function help_get_num_lines(node) {
+  let style = mc.window.getComputedStyle(node, null);
+  return style.height / style.lineHeight;
+}
+
+/**
+ * Test that the "more" widget displays when it should.
+ * @param toDescription the description node for the "to" field
+ */
+function subtest_more_widget_display(toDescription) {
+  // test that the to element doesn't have more than max lines
+  let numLines = help_get_num_lines(toDescription);
+
+  // get maxline pref
+  let prefBranch = Cc["@mozilla.org/preferences-service;1"]
+    .getService(Ci.nsIPrefService).getBranch(null);
+  let maxLines = prefBranch.getIntPref(
+    "mailnews.headers.show_n_lines_before_more");
+
+  // allow for a 15% tolerance for any padding that may be applied
+  if (numLines < 0.85*maxLines || numLines > 1.15*maxLines) {
+    throw new Error("expected == " + maxLines + "lines; found " + numLines);
+  }
+
+  // test that we've got a (more) node and that it's expanded
+  let moreNode = mc.a('expandedtoBox', {class: 'moreIndicator'});
+  if (!moreNode) {
+    throw new Error("more node not found before activation");
+  }
+  if (moreNode.collapsed) {
+    throw new Error("more node was collapsed when it should have been visible");
+  }
+}
+
+/**
+ * Test that clicking the "more" widget displays all the addresses.
+ * @param toDescription the description node for the "to" field
+ */
+function subtest_more_widget_click(toDescription) {
+  let oldNumLines = help_get_num_lines(toDescription);
+
+  // activate (n more)
+  let moreNode = mc.aid('expandedtoBox', {class: 'moreIndicator'});
+  mc.click(moreNode);
+
+  // test that (n more) is gone
+  moreNode = mc.a('expandedtoBox', {class: 'moreIndicator'});
+  if (!moreNode.collapsed) {
+    throw new Error("more node should be collapsed after activation");
+  }
+
+  // test that we actually have more lines than we did before!
+  let newNumLines = help_get_num_lines(toDescription);
+  if (newNumLines <= oldNumLines) {
+    throw new Error("number of address lines present after more clicked = " +
+      newNumLines + "<= number of lines present beforehand = " + oldNumLines);
+  }
+}
+
+/**
+ * Test that changing to all header lines mode displays all the addresses.
+ * @param toDescription the description node for the "to" field
+ */
+function subtest_change_to_all_header_mode(toDescription) {
+  let oldNumLines = help_get_num_lines(toDescription);
+
+  change_to_all_header_mode();
+  // test that (n more) is gone
+  let moreNode = mc.a('expandedtoBox', {class: 'moreIndicator'});
+  if (!moreNode.collapsed) {
+    throw new Error("more node should be collapsed in all header lines mode");
+  }
+
+  // test that we actually have more lines than we did before!
+  let newNumLines = help_get_num_lines(toDescription);
+  if (newNumLines <= oldNumLines) {
+    throw new Error("number of address lines present in all header lines mode = " +
+      newNumLines + "<= number of lines present beforehand = " + oldNumLines);
+  }
+}
+
+/**
+ * Test that clicking the star updates the UI properly (see bug 563612).
+ * @param toDescription the description node for the "to" field
+ */
+function subtest_more_widget_star_click(toDescription) {
+  let addrs = toDescription.getElementsByTagName('mail-emailaddress');
+  let lastAddr = addrs[addrs.length-1];
+  ensure_no_card_exists(lastAddr.getAttribute("emailAddress"));
+
+  // scroll to the bottom first so the address is in view
+  let view = mc.e('expandedHeaderView');
+  view.scrollTop = view.scrollHeight - view.clientHeight;
+
+  mc.click(mc.aid(lastAddr, {class: 'emailStar'}));
+  if (lastAddr.getAttribute('hascard') == 'false') {
+    throw new Error("address not updated after clicking star");
+  }
+}
+
+/**
+ * Make sure the (more) widget hidden pref actually works with a
+ * non-default value.
+ */
+function test_more_widget_with_maxlines_of_3(){
+
+  // set maxLines to 3
+  let prefBranch = Cc["@mozilla.org/preferences-service;1"]
+    .getService(Ci.nsIPrefService).getBranch(null);
+  let maxLines = prefBranch.setIntPref(
+    "mailnews.headers.show_n_lines_before_more", 3);
+
+  // call test_more_widget again
+  test_more_widget();
+}
+
+/**
+ * Make sure the (more) widget hidden pref also works with an
+ * "all" (0) non-default value.
+ */
+function test_more_widget_with_disabled_more(){
+
+  // set maxLines to 0
+  let prefBranch = Cc["@mozilla.org/preferences-service;1"]
+    .getService(Ci.nsIPrefService).getBranch(null);
+  let maxLines = prefBranch.setIntPref(
+    "mailnews.headers.show_n_lines_before_more", 0);
+
+  // generate message with 35 recips (effectively guarantees overflow for n=3)
+  be_in_folder(folder);
+  let msg = create_message({toCount: 35});
+
+  // add the message to the end of the folder
+  add_message_to_folder(folder, msg);
+
+  // select and open the last message
+  let curMessage = select_click_row(-1);
+
+  // make sure it loads
+  wait_for_message_display_completion(mc);
+  assert_selected_and_displayed(mc, curMessage);
+
+  // test that (n more) is gone
+  let moreNode = mc.a('expandedtoBox', {class: 'moreIndicator'});
+  if (!moreNode.collapsed) {
+    throw new Error("more node should be collapsed in n=0 case");
+  }
+
+  // get the description element containing the addresses
+  let toDescription = mc.a('expandedtoBox', {class: "headerValue"});
+
+  // test that we actually have more lines than the 3 we know are filled
+  let newNumLines = help_get_num_lines(toDescription);
+  if (newNumLines <= 3) {
+    throw new Error("number of address lines present in all addresses mode = " +
+      newNumLines + "<= number of expected minimum of 3 lines filled");
+  }
+}
+
+/**
+ * When the window gets too narrow the toolbar should float above the From
+ *  line.  Then they need to return back to the right when we get large
+ *  enough again.
+ */
+function test_toolbar_collapse_and_expand() {
+  be_in_folder(folder);
+  // Select and open a message, in this case the last, for no particular reason.
+  let curMessage = select_click_row(-1);
+
+  try {
+    let expandedHeadersTopBox = mc.e("expandedHeadersTopBox");
+    let toolbar = mc.e("header-view-toolbar");
+    let mode = toolbar.getAttribute("mode");
+
+    // Get really big, so that we can figure out how big we actually want to be.
+    mc.window.resizeTo(1200, 600);
+    // spin the event loop once
+    mc.sleep(0);
+
+    let folderPaneWidth = mc.e("folderPaneBox").clientWidth;
+    let fromWidth = mc.e("expandedfromRow").clientWidth;
+
+    // This is the biggest we need to be.
+    let bigWidth = folderPaneWidth + fromWidth + toolbar.clientWidth;
+
+    // Now change to icons-only mode for a much smaller toolbar.
+    toolbar.setAttribute("mode", "icons");
+    let smallWidth = folderPaneWidth + fromWidth + toolbar.clientWidth;
+
+    // Re-set the mode to its original value.
+    toolbar.setAttribute("mode", mode);
+
+    // And resize to half way between the big and small widths, so that we
+    //  can toggle the mode to force the overflow.
+    mc.window.resizeTo((bigWidth + smallWidth) / 2, 600);
+    // spin the event loop once
+    mc.sleep(0);
+
+    // Make sure we are too small to contain the buttons and from line, so
+    //  we will be tall.
+    let tallHeight = expandedHeadersTopBox.clientHeight;
+
+    // Change from icons and text to just icons to make our toolbar
+    //  narrower, and by extension our header shorter.
+    toolbar.setAttribute("mode", "icons");
+
+    let shortHeight = expandedHeadersTopBox.clientHeight;
+    if (shortHeight >= tallHeight)
+      throw new Error("The header box should have been made smaller!");
+
+    // Change back to icons and text to make our toolbar wider and our
+    //   header taller again.
+    toolbar.setAttribute("mode", mode);
+    if (expandedHeadersTopBox.clientHeight != tallHeight)
+      throw new Error("The header box should have returned to its original size!");
+
+    // And make our window big to achieve the same effect as the just icons mode.
+    mc.window.resizeTo(1200, 600);
+    // spin the event loop once
+    mc.sleep(0);
+    if (expandedHeadersTopBox.clientHeight != shortHeight)
+      throw new Error("The header box should have returned to its wide size!");
+  }
+  finally {
+    // restore window to nominal dimensions; saving was not working out
+    //  See also: quick-filter-bar/test-display-issues.js if we change the
+    //            default window size.
+    mc.window.resizeTo(1024, 768);
+  }
+}
diff --git a/test/compactheader/test-mouse-event-helpers.js b/test/compactheader/test-mouse-event-helpers.js
new file mode 100644
index 0000000..c46ed51
--- /dev/null
+++ b/test/compactheader/test-mouse-event-helpers.js
@@ -0,0 +1,220 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird Mail Client.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mark Banner <bugzilla at standard8.plus.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cu = Components.utils;
+
+var elib = {};
+Cu.import('resource://mozmill/modules/elementslib.js', elib);
+var mozmill = {};
+Cu.import('resource://mozmill/modules/mozmill.js', mozmill);
+var EventUtils = {};
+Cu.import('resource://mozmill/stdlib/EventUtils.js', EventUtils);
+
+const MODULE_NAME = 'mouse-event-helpers';
+
+
+function setupModule() {
+}
+
+function installInto(module) {
+  setupModule();
+
+  // Now copy helper functions
+  module.drag_n_drop_element = drag_n_drop_element;
+  module.synthesize_drag_start = synthesize_drag_start;
+  module.synthesize_drag_over = synthesize_drag_over;
+  module.synthesize_drag_end = synthesize_drag_end;
+  module.synthesize_drop = synthesize_drop;
+}
+
+/**
+ * Execute a drag and drop session.
+ * @param {XULElement} aDragObject
+ *   the element from which the drag session should be started.
+ * @param {} aDragWindow
+ *   the window the aDragObject is in
+ * @param {XULElement} aDropObject
+ *   the element at which the drag session should be ended.
+ * @param {} aDropWindow
+ *   the window the aDropObject is in
+ * @param {} aRelDropX
+ *   the relative x-position the element is dropped over the aDropObject
+ * @param {} aRelDropY
+ *   the relative y-position the element is dropped over the aDropObject
+ * @param {XULElement} aListener
+ *   the element who's drop target should be captured and returned.
+ */
+function drag_n_drop_element(aDragObject, aDragWindow, aDropObject, 
+                             aDropWindow, aRelDropX, aRelDropY, aListener)
+{
+  let dt = synthesize_drag_start(aDragWindow, aDragObject, aListener);
+
+  // Drop it onto the third tab ...
+  synthesize_drag_over(aDropWindow, aDropObject, dt);
+
+  synthesize_drop(aDropWindow, aDropObject, dt,
+      { screenX : aDropObject.boxObject.screenX +
+                    (aDropObject.boxObject.width * aRelDropX),
+        screenY : aDropObject.boxObject.screenY +
+                    (aDropObject.boxObject.width * aRelDropY)
+      });
+}
+
+/**
+ * Starts a drag new session.
+ * @param {} aWindow
+ * @param {XULElement} aDispatcher
+ *   the element from which the drag session should be started.
+ * @param {XULElement} aListener
+ *   the element who's drop target should be captured and returned.
+ * @return {nsIDataTransfer}
+ *   returns the DataTransfer Object of captured by aListener.
+ */
+function synthesize_drag_start(aWindow, aDispatcher, aListener)
+{
+  let dt;
+
+  var trapDrag = function(event) {
+
+    if ( !event.dataTransfer )
+      throw "no DataTransfer";
+
+    dt = event.dataTransfer;
+
+    //event.stopPropagation();
+    event.preventDefault();
+  };
+
+  aListener.addEventListener("dragstart", trapDrag, true);
+
+  EventUtils.synthesizeMouse(aDispatcher, 5, 5, {type:"mousedown"}, aWindow);
+  EventUtils.synthesizeMouse(aDispatcher, 5, 10, {type:"mousemove"}, aWindow);
+  EventUtils.synthesizeMouse(aDispatcher, 5, 15, {type:"mousemove"}, aWindow);
+
+  aListener.removeEventListener("dragstart", trapDrag, true);
+
+  return dt;
+}
+
+/**
+ * Synthesizes a drag over event.
+ * @param {} aWindow
+ * @param {XULElement} aDispatcher
+ *   the element from which the drag session should be started.
+ * @param {nsIDataTransfer} aDt
+ *   the DataTransfer Object of captured by listener.
+ * @param {} aArgs
+ *   arguments passed to the mouse event.
+ */
+function synthesize_drag_over(aWindow, aDispatcher, aDt, aArgs)
+{
+  _synthesizeDragEvent("dragover", aWindow, aDispatcher, aDt, aArgs);
+}
+
+/**
+ * Synthesizes a drag end event.
+ * @param {} aWindow
+ * @param {XULElement} aDispatcher
+ *   the element from which the drag session should be started.
+ * @param {nsIDataTransfer} aDt
+ *   the DataTransfer Object of captured by listener.
+ * @param {} aArgs
+ *   arguments passed to the mouse event.
+ */
+function synthesize_drag_end(aWindow, aDispatcher, aListener, aDt, aArgs)
+{
+  _synthesizeDragEvent("dragend", aWindow, aListener, aDt, aArgs);
+
+  //Ensure drag has ended.
+  EventUtils.synthesizeMouse(aDispatcher, 5, 5, {type:"mousemove"}, aWindow);
+  EventUtils.synthesizeMouse(aDispatcher, 5, 10, {type:"mousemove"}, aWindow);
+  EventUtils.synthesizeMouse(aDispatcher, 5, 5, {type:"mouseup"}, aWindow);
+}
+
+/**
+ * Synthesizes a drop oevent.
+ * @param {} aWindow
+ * @param {XULElement} aDispatcher
+ *   the element from which the drag session should be started.
+ * @param {nsIDataTransfer} aDt
+ *   the DataTransfer Object of captured by listener.
+ * @param {} aArgs
+ *   arguments passed to the mouse event.
+ */
+function synthesize_drop(aWindow, aDispatcher, aDt, aArgs)
+{
+  _synthesizeDragEvent("drop", aWindow, aDispatcher, aDt, aArgs);
+
+  // Ensure drag has ended.
+  EventUtils.synthesizeMouse(aDispatcher, 5, 5, {type:"mousemove"}, aWindow);
+  EventUtils.synthesizeMouse(aDispatcher, 5, 10, {type:"mousemove"}, aWindow);
+  EventUtils.synthesizeMouse(aDispatcher, 5, 5, {type:"mouseup"}, aWindow);
+}
+
+/**
+ * Private function: Synthesizes a specified drag event.
+ * @param {} aType
+ *   the type of the drag event to be synthesiyzed.
+ * @param {} aWindow
+ * @param {XULElement} aDispatcher
+ *   the element from which the drag session should be started.
+ * @param {nsIDataTransfer} aDt
+ *   the DataTransfer Object of captured by listener.
+ * @param {} aArgs
+ *   arguments passed to the mouse event.
+ */
+function _synthesizeDragEvent(aType, aWindow, aDispatcher, aDt, aArgs)
+{
+  let screenX;
+  if (aArgs && ("screenX" in aArgs))
+    screenX = aArgs.screenX;
+  else
+    screenX = aDispatcher.boxObject.ScreenX;;
+
+  let screenY;
+  if (aArgs && ("screenY" in aArgs))
+    screenY = aArgs.screenY;
+  else
+    screenY = aDispatcher.boxObject.ScreenY;
+
+  let event = aWindow.document.createEvent("DragEvents");
+  event.initDragEvent(aType, true, true, aWindow, 0,
+      screenX, screenY, 0, 0, false, false, false, false, 0, null, aDt);
+  aDispatcher.dispatchEvent(event);
+}
diff --git a/test/download.sh b/test/download.sh
new file mode 100644
index 0000000..23dbdaf
--- /dev/null
+++ b/test/download.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+
+export FTP_DIR=https://ftp.mozilla.org/pub/mozilla.org/thunderbird/nightly/5.0-candidates/build1/unsigned/win32/en-US/
+export APP=thunderbird-5.0.zip
+export TESTS=thunderbird-5.0.tests.zip
+
+wget -P ftp -N $FTP_DIR/$APP
+wget -P ftp -N $FTP_DIR/$TESTS
+
+export TESTDIR=test-5.0
+mkdir -p $TESTDIR
+
+unzip -o ftp/$APP -d $TESTDIR
+unzip -o ftp/$TESTS -d $TESTDIR -x "*mochitest*" "*xpcshell*"
+
+junction $TESTDIR/mozmill/compactheader compactheader
+# copy drag'n'drop helpers to shared-modules until they are added to thunderbird source
+cp $TESTDIR/mozmill/compactheader/test-mouse-event-helpers.js $TESTDIR/mozmill/shared-modules
+
+# python runtest.py --binary=../thunderbird/thunderbird.exe  -a ../../../AMO/CompactHeader-1.4.2beta3.xpi -l log -t compactheader/test-compactheader-toolbar.js
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/compactheader.git



More information about the Pkg-mozext-commits mailing list