From cce79a6c421b6ba07da1de0be4137d704023e78d Mon Sep 17 00:00:00 2001 From: Joachim Herb Date: Sat, 27 Aug 2011 16:43:05 +0100 Subject: [PATCH] Bug 519956 - Finalize customization of header pane toolbar, needs palette and View toggle. r=sid0 --- .../message-header/test-header-toolbar.js | 557 ++++++++++++++++++ .../message-header/test-message-header.js | 47 -- .../test-mouse-event-helpers.js | 221 +++++++ .../mozmill/tabmail/test-tabmail-dragndrop.js | 111 +--- 4 files changed, 793 insertions(+), 143 deletions(-) create mode 100644 mail/test/mozmill/message-header/test-header-toolbar.js create mode 100644 mail/test/mozmill/shared-modules/test-mouse-event-helpers.js diff --git a/mail/test/mozmill/message-header/test-header-toolbar.js b/mail/test/mozmill/message-header/test-header-toolbar.js new file mode 100644 index 0000000000..1f43bb8ff9 --- /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 + * Dan Mosedale + * Joachim Herb + * + * 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 ", + "Content-Base": "http://example.com/", + "Bcc": "Richard Roe " + }}); + + 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"); +} diff --git a/mail/test/mozmill/message-header/test-message-header.js b/mail/test/mozmill/message-header/test-message-header.js index b9819390e7..c33fdfba11 100644 --- a/mail/test/mozmill/message-header/test-message-header.js +++ b/mail/test/mozmill/message-header/test-message-header.js @@ -691,53 +691,6 @@ function test_toolbar_collapse_and_expand() { 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) { /** diff --git a/mail/test/mozmill/shared-modules/test-mouse-event-helpers.js b/mail/test/mozmill/shared-modules/test-mouse-event-helpers.js new file mode 100644 index 0000000000..cc0b4e3317 --- /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 + * Joachim Herb + * + * 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); +} diff --git a/mail/test/mozmill/tabmail/test-tabmail-dragndrop.js b/mail/test/mozmill/tabmail/test-tabmail-dragndrop.js index ea32dcf9c1..d0a6b87136 100644 --- a/mail/test/mozmill/tabmail/test-tabmail-dragndrop.js +++ b/mail/test/mozmill/tabmail/test-tabmail-dragndrop.js @@ -44,7 +44,9 @@ Cu.import('resource://mozmill/modules/elementslib.js', elib); 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 = []; @@ -57,6 +59,8 @@ function setupModule(module) { 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}]); @@ -133,12 +137,12 @@ function test_tab_reorder_tabbar(){ 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 }); @@ -221,11 +225,11 @@ function test_tab_reorder_window(){ 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 }); @@ -270,12 +274,12 @@ function test_tab_reorder_detach(){ 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 ) }); @@ -481,90 +485,5 @@ function teardownTest(test) 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); + }