Bug 519956 - Finalize customization of header pane toolbar, needs palette and View toggle. r=sid0
authorJoachim Herb <herb@leo.org>
Sat, 27 Aug 2011 16:43:05 +0100
changeset 9062 72265fa31f49918ef1d2cf738b952e8eaf9ac2aa
parent 9061 88fc3d12b3b768c1f8c4f6258cfc9626b431eb9a
child 9063 27529ca52af4142e4e6a45c02c70fde93aa5ef7a
push id230
push userbugzilla@standard8.plus.com
push dateTue, 08 Nov 2011 22:55:24 +0000
treeherdercomm-beta@63dad5648415 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssid0
bugs519956
Bug 519956 - Finalize customization of header pane toolbar, needs palette and View toggle. r=sid0
mail/test/mozmill/message-header/test-header-toolbar.js
mail/test/mozmill/message-header/test-message-header.js
mail/test/mozmill/shared-modules/test-mouse-event-helpers.js
mail/test/mozmill/tabmail/test-tabmail-dragndrop.js
new file mode 100644
--- /dev/null
+++ b/mail/test/mozmill/message-header/test-header-toolbar.js
@@ -0,0 +1,557 @@
+/* ***** 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blake Winton <bwinton@latte.ca>
+ *   Dan Mosedale <dmose@mozillamessaging.com>
+ *   Joachim Herb <Joachim.Herb@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);
+Cu.import("resource://gre/modules/Services.jsm");
+
+var folder;
+
+const USE_SHEET_PREF = "toolbar.customization.usesheet";
+
+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);
+  let meh = collector.getModule('mouse-event-helpers');
+  meh.installInto(module);
+
+  folder = create_folder("HeaderToolbar");
+
+  // 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),
+                            subject: "This is a really, really, really, really, really, really, really, really, long subject.",
+                            clobberHeaders: {
+                              "Newsgroups": "alt.test",
+                              "Reply-To": "J. Doe <j.doe@momo.invalid>",
+                              "Content-Base": "http://example.com/",
+                              "Bcc": "Richard Roe <richard.roe@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 open the Get Message button's menu to get the popup menu
+  // populated
+  mc.click(mc.aid("button-getmsg", {class: "toolbarbutton-menubutton-dropmarker"}));
+  mc.ewait("button-getAllNewMsgSeparator");
+
+  let getMailButtonPopup = mc.eid("button-getMsgPopup").node;
+  let 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;
+  let finalServerCount = getMailButtonPopup.childElementCount;
+
+  assert_equals(finalServerCount, originalServerCount,
+                "number of entries in Get Message Button popup menu after " +
+                "header toolbar customization not equal as before");
+}
+
+/**
+ *  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);
+  hdrToolbar = msgc.eid("header-view-toolbar").node;
+  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 = toolbar.currentSet.split(",");
+
+  let ctc = open_header_pane_toolbar_customization(mc);
+  let currentSet = 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 the button which was placed left of it
+    // at the beginning of the test starting with the second button. This
+    // places the buttons in the reverse order as at the beginning of the test.
+    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(toolbar.currentSet, reverseSet);
+  assert_equals(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 = toolbar.currentSet.split(",");
+
+  let ctc = open_header_pane_toolbar_customization(msgc);
+  let currentSet = 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 the button which was placed left of it
+    // at the beginning of the test starting with the second button. This
+    // places the buttons in the reverse order as at the beginning of the test.
+    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(toolbar.currentSet, reverseSet);
+  assert_equals(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.
+  close_window(mc);
+  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.
+  hdrToolbar = mc.eid("header-view-toolbar").node;
+  hdrBarDefaultSet = hdrToolbar.getAttribute("defaultset");
+  assert_equals(hdrToolbar.currentSet, hdrBarDefaultSet);
+  assert_equals(hdrToolbar.getAttribute("currentset"), hdrBarDefaultSet);
+
+  // Open separate mail window again and check another time.
+  msgc = open_selected_message_in_new_window();
+  assert_selected_and_displayed(msgc, curMessage);
+  toolbar = msgc.eid("header-view-toolbar").node;
+  assert_equals(toolbar.currentSet, reverseSet);
+  assert_equals(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.
+  let 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 = 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.
+  toolbar = mc.eid("header-view-toolbar").node;
+  assert_equals(toolbar.currentSet, "__empty");
+  assert_equals(toolbar.getAttribute("currentset"), "__empty");
+
+  // Move to the next message and Check again.
+  let curMessage = select_message_in_folder(1);
+  assert_equals(toolbar.currentSet, "__empty");
+  assert_equals(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 button persistance
+
+  // 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.
+  close_window(mc);
+  mc = open3PaneWindow();
+  abwc.window.close();
+  select_message_in_folder(0);
+
+  toolbar = mc.eid("header-view-toolbar").node;
+  assert_equals(toolbar.currentSet, "__empty");
+  assert_equals(toolbar.getAttribute("currentset"), "__empty");
+
+  // Check that all removed buttons show up in the palette
+  // and move it back in the toolbar.
+  ctc = open_header_pane_toolbar_customization(mc);
+  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);
+
+  toolbar = mc.eid("header-view-toolbar").node;
+  assert_equals(toolbar.currentSet, hdrBarDefaultSet);
+  assert_equals(toolbar.getAttribute("currentset"), 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 text besides icon menulist entry is selected, because in the header toolbar
+  // this is the default mode.
+  let textIconMode = ctc.eid("textbesideiconItem").node;
+  assert_equals(textIconMode.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 label and icon visible.
+  subtest_buttons_style("-moz-box", "-moz-box");
+
+  // Change the button style to icon (only) mode
+  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
+  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 label and icon visible.
+  restore_and_check_default_buttons(mc);
+  subtest_buttons_style("-moz-box", "-moz-box");
+}
+
+/**
+ * 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 = 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 (Services.prefs.getBoolPref(USE_SHEET_PREF, true)) {
+    // XXX Sleep so the dialog has a chance to load. It seems that
+    // ewait("donebutton") does not work after the update to mozmill 1.5.4b4.
+    controller.sleep(1000);
+    let contentWindow = aController.eid("customizeToolbarSheetIFrame").node.contentWindow;
+    // This is taken from test-migration-helpers.js#128:
+    // XXX this is not my fault, but I'm not going to fix it. Just make it less
+    // broken:
+    // Lie to mozmill to convince it to not explode because these frames never
+    // get a mozmillDocumentLoaded attribute.
+    contentWindow.mozmillDocumentLoaded = true;
+    ctc = augment_controller(new controller.MozMillController(contentWindow));
+  }
+  else {
+    ctc = wait_for_existing_window("CustomizeToolbarWindow");
+  }
+  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 (!Services.prefs.getBoolPref(USE_SHEET_PREF, true)) {
+    assert_true(aCtc.window.closed, "The customization dialog is not closed.");
+  }
+}
+
+/**
+ *  Helper functions 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 open3PaneWindow()
+{
+  plan_for_new_window("mail:3pane");
+  Services.ww.openWindow(null,
+                         "chrome://messenger/content/messenger.xul", "",
+                         "all,chrome,dialog=no,status,toolbar",
+                         null);
+  return wait_for_new_window("mail:3pane");
+}
+
+function openAddressBook()
+{
+  plan_for_new_window("mail:addressbook");
+  Services.ww.openWindow(null,
+                         "chrome://messenger/content/addressbook/addressbook.xul",
+                         "", "all,chrome,dialog=no,status,toolbar",
+                         null);
+  return wait_for_new_window("mail:addressbook");
+}
--- a/mail/test/mozmill/message-header/test-message-header.js
+++ b/mail/test/mozmill/message-header/test-message-header.js
@@ -686,63 +686,16 @@ function test_toolbar_collapse_and_expan
   }
   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);
   }
 }
-
-/**
- *  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(){
-  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);
-
-  // 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
-  mc.click(mc.eid("CustomizeHeaderToolbar"));
-  let toolbox = mc.eid("header-view-toolbox").node;
-
-  // Due to differences between OS X and Windows/Linux versions
-  // the "done" button of the customization dialog cannot be
-  // accessed directly
-  toolbox.customizeDone();
-
-  // 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);
-  }
-}
-
 // Some platforms (notably Mac) don't have a11y, so disable these tests there.
 if ("nsIAccessibleRole" in Ci) {
   /**
    * @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
new file mode 100644
--- /dev/null
+++ b/mail/test/mozmill/shared-modules/test-mouse-event-helpers.js
@@ -0,0 +1,221 @@
+/* ***** 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mark Banner <bugzilla@standard8.plus.com>
+ *   Joachim Herb <Joachim.Herb@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 ***** */
+
+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
+ *   in percent of the aDropObject width
+ * @param {} aRelDropY
+ *   the relative y-position the element is dropped over the aDropObject
+ *   in percent of the aDropObject height
+ * @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);
+
+  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;
+
+  let trapDrag = function(event) {
+
+    if (!event.dataTransfer)
+      throw "no DataTransfer";
+
+    dt = event.dataTransfer;
+
+    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 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_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);
+}
--- a/mail/test/mozmill/tabmail/test-tabmail-dragndrop.js
+++ b/mail/test/mozmill/tabmail/test-tabmail-dragndrop.js
@@ -39,29 +39,33 @@ Cu.import('resource://mozmill/modules/el
 
 /*
  * Test rearanging tabs via drag'n'drop.
  */
 
 var MODULE_NAME = "test-tabmail-dragndrop";
 
 var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["folder-display-helpers", "window-helpers"];
+var MODULE_REQUIRES = ["folder-display-helpers", "window-helpers",
+                       'mouse-event-helpers'];
+
 
 var folder;
 let msgHdrsInFolder = [];
 
 // The number of messages in folder.
 const NUM_MESSAGES_IN_FOLDER = 15;
 
 function setupModule(module) {
   let fdh = collector.getModule("folder-display-helpers");
   fdh.installInto(module);
   let wh = collector.getModule("window-helpers");
   wh.installInto(module);
+  let meh = collector.getModule('mouse-event-helpers');
+  meh.installInto(module);
 
   folder = create_folder("MessageFolder");
   make_new_sets_in_folder(folder, [{count: NUM_MESSAGES_IN_FOLDER}]);
 }
 
 /**
  * Verifies our test environment is setup correctly and initializes
  * all global variables.
@@ -128,22 +132,22 @@ function test_tab_reorder_tabbar(){
 
   // Start dragging the first tab
   switch_tab(1);
   assert_selected_and_displayed(msgHdrsInFolder[0]);
 
   let tab1 = mc.tabmail.tabContainer.childNodes[1];
   let tab3 = mc.tabmail.tabContainer.childNodes[3];
 
-  let dt = _synthesizeDragStart(mc.window, tab1, mc.tabmail);
+  let dt = synthesize_drag_start(mc.window, tab1, mc.tabmail);
 
   // Drop it onto the third tab ...
-  _synthesizeDragOver(mc.window, tab3, dt);
+  synthesize_drag_over(mc.window, tab3, dt);
 
-  _synthesizeDrop(mc.window, tab3, dt,
+  synthesize_drop(mc.window, tab3, dt,
       { screenX : tab3.boxObject.screenX + (tab3.boxObject.width * 0.75),
         screenY : tab3.boxObject.screenY });
 
   wait_for_message_display_completion(mc);
 
   // if every thing went well...
   assert_number_of_tabs_open(5);
 
@@ -216,21 +220,21 @@ function test_tab_reorder_window(){
   // Start dragging the first tab ...
   let tabA = mc.tabmail.tabContainer.childNodes[1];
   assert_true(tabA, "No movable Tab");
 
   // We drop onto the Folder Tab, it is guaranteed to exist.
   let tabB = mc2.tabmail.tabContainer.childNodes[0];
   assert_true(tabB, "No movable Tab");
 
-  let dt = _synthesizeDragStart(mc.window,tabA,mc.tabmail);
+  let dt = synthesize_drag_start(mc.window,tabA,mc.tabmail);
 
-  _synthesizeDragOver(mc2.window, tabB,dt);
+  synthesize_drag_over(mc2.window, tabB,dt);
 
-  _synthesizeDrop(mc2.window,tabB, dt,
+  synthesize_drop(mc2.window,tabB, dt,
       { screenX : tabB.boxObject.screenX + (tabB.boxObject.width * 0.75),
         screenY : tabB.boxObject.screenY });
 
   wait_for_message_display_completion(mc2);
 
   assert_true( !! (mc.tabmail.tabContainer.childNodes.length == 1),
     "Moving tab to new window failed, tab still in old window");
 
@@ -265,22 +269,22 @@ function test_tab_reorder_detach(){
   // ... now start dragging
 
   mc.tabmail.switchToTab(1);
 
   let tab1 = mc.tabmail.tabContainer.childNodes[1];
   let dropContent = mc.e("tabpanelcontainer");
   let box = dropContent.boxObject;
 
-  let dt = _synthesizeDragStart(mc.window, tab1, mc.tabmail);
+  let dt = synthesize_drag_start(mc.window, tab1, mc.tabmail);
 
-  _synthesizeDragOver(mc.window, dropContent, dt);
+  synthesize_drag_over(mc.window, dropContent, dt);
 
   // notify tab1 drag has ended
-  _synthesizeDragEnd(mc.window, dropContent, tab1, dt,
+  synthesize_drag_end(mc.window, dropContent, tab1, dt,
       { screenX : (box.screenX + box.width / 2 ),
         screenY : (box.screenY + box.height / 2 ) });
 
   // ... and wait for the new window
   mc2 = wait_for_new_window("mail:3pane");
   wait_for_message_display_completion(mc2, true);
 
   assert_true(mc.tabmail.tabContainer.childNodes.length == 1,
@@ -476,95 +480,10 @@ function teardownTest(test)
         
     case test_tab_recentlyClosed :
     case test_tab_undo :
     
       // clean up the tabbbar 
       mc.tabmail.closeOtherTabs(0);
       assert_number_of_tabs_open(1);
   }
-  
-}
 
-/*
- * A set of private helper functions for drag'n'drop
- */
-
-/**
- * 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 _synthesizeDragStart(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;
 }
-
-function _synthesizeDragOver(aWindow, aDispatcher, aDt, aArgs)
-{
-  _synthesizeDragEvent("dragover", aWindow, aDispatcher, aDt, aArgs);
-}
-
-function _synthesizeDragEnd(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);
-}
-
-function _synthesizeDrop(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);
-}
-
-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);
-}