/* ***** 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 MozMill Test code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Henrik Skupin * * 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 ***** */ // Include required modules var domUtils = require("dom-utils"); var tabs = require("tabs"); var utils = require("utils"); const TIMEOUT = 5000; /** * Constructor */ function tabView(aController) { this._controller = aController; this._tabView = null; this._tabViewDoc = this._controller.window.document; this._tabViewObject = this._controller.window.TabView; } /** * Tab View class */ tabView.prototype = { /////////////////////////////// // Global section /////////////////////////////// /** * Returns the MozMill controller * * @returns Mozmill controller * @type {MozMillController} */ get controller() { return this._controller; }, /** * Check if the Tab View is open * * @returns True if the Tab View is open * @type {boolean} */ get isOpen() { var deck = this.getElement({type: "deck"}); return deck.getNode().getAttribute("selectedIndex") == "1"; }, /** * Open the Tab View */ open : function tabView_open() { var menuitem = new elementslib.Elem(this._controller.menus['view-menu'].menu_tabview); this._controller.click(menuitem); this.waitForOpened(); this._tabView = this.getElement({type: "tabView"}); this._tabViewDoc = this._tabView.getNode().webNavigation.document; }, /** * Reset the Tab View settings for the current window */ reset : function tabView_reset() { // Make sure to close TabView before resetting its ui if (this.isOpen) { this.close(); } var self = this; this._tabViewObject._initFrame(function () { var contentWindow = self._tabViewObject._window; contentWindow.UI.reset(); }); // Make sure all tabs will be shown Array.forEach(this._controller.window.gBrowser.tabs, function (tab) { this._controller.window.gBrowser.showTab(tab); }, this); }, /** * Wait until the Tab View has been opened */ waitForOpened : function tabView_waitForOpened() { // Add event listener to wait until the tabview has been opened var self = { opened: false }; function checkOpened() { self.opened = true; } this._controller.window.addEventListener("tabviewshown", checkOpened, false); try { mozmill.utils.waitFor(function () { return self.opened == true; }, "TabView is not open."); this._groupItemsObject = this._tabViewObject._window.GroupItems; this._tabItemsObject = this._tabViewObject._window.TabItems; } finally { this._controller.window.removeEventListener("tabviewshown", checkOpened, false); } }, /** * Close the Tab View */ close : function tabView_close() { var menuitem = new elementslib.Elem(this._controller.menus['view-menu'].menu_tabview); this._controller.click(menuitem); this.waitForClosed(); this._tabView = null; this._tabViewDoc = this._controller.window.document; }, /** * Wait until the Tab View has been closed */ waitForClosed : function tabView_waitForClosed() { // Add event listener to wait until the tabview has been closed var self = { closed: false }; function checkClosed() { self.closed = true; } this._controller.window.addEventListener("tabviewhidden", checkClosed, false); try { mozmill.utils.waitFor(function () { return self.closed == true; }, "TabView is still open."); } finally { this._controller.window.removeEventListener("tabviewhidden", checkClosed, false); } this._groupItemsObject = null; this._tabItemsObject = null; }, /////////////////////////////// // Groups section /////////////////////////////// /** * Returns the tab groups which match the filter criteria * * @param {object} aSpec * Information about the filter to apply * Elements: filter - Type of filter to apply * (active, title) * [optional - default: ""] * value - Value of the element * [optional - default: ""] * * @returns List of groups * @type {array of ElemBase} */ getGroups : function tabView_getGroups(aSpec) { var spec = aSpec || {}; return this.getElements({ type: "groups", subtype: spec.filter, value: spec.value }); }, /** * Retrieve the group's title box * * @param {object} aSpec * Information on which group to operate on * Elements: group - Group element * * @returns Group title box * @type {ElemBase} */ getGroupTitleBox : function tabView_getGroupTitleBox(aSpec) { var spec = aSpec || {}; var group = spec.group; if (!group) { throw new Error(arguments.callee.name + ": Group not specified."); } return this.getElement({ type: "group_titleBox", parent: group }); }, /** * Close the specified tab group * * @param {object} aSpec * Information on which group to operate on * Elements: group - Group */ closeGroup : function tabView_closeGroup(aSpec) { var spec = aSpec || {}; var group = spec.group; if (!group) { throw new Error(arguments.callee.name + ": Group not specified."); } var button = this.getElement({ type: "group_closeButton", parent: group }); this._controller.click(button); this.waitForGroupClosed({group: group}); }, /** * Wait until the specified tab group has been closed * * @param {object} aSpec * Information on which group to operate on * Elements: group - Group */ waitForGroupClosed : function tabView_waitForGroupClosed(aSpec) { var spec = aSpec || {}; var group = spec.group; var groupObj = null; var self = { closed: false }; function checkClosed() { self.closed = true; } if (!group) { throw new Error(arguments.callee.name + ": Group not specified."); } this._groupItemsObject.groupItems.forEach(function (node) { if (node.container == group.getNode()) { groupObj = node; } }); if (!groupObj) { throw new Error(arguments.callee.name + ": Group not found."); } try { groupObj.addSubscriber(groupObj, "groupHidden", checkClosed); mozmill.utils.waitFor(function () { return self.closed; }, "Tab Group has not been closed."); } finally { groupObj.removeSubscriber(groupObj, "groupHidden"); } }, /** * Undo the closing of the specified tab group * * @param {object} aSpec * Information on which group to operate on * Elements: group - Group */ undoCloseGroup : function tabView_undoCloseGroup(aSpec) { var spec = aSpec || {}; var group = spec.group; if (!group) { throw new Error(arguments.callee.name + ": Group not specified."); } var undo = this.getElement({ type: "group_undoButton", parent: group }); this._controller.waitThenClick(undo); this.waitForGroupUndo({group: group}); }, /** * Wait until the specified tab group has been reopened * * @param {object} aSpec * Information on which group to operate on * Elements: group - Group */ waitForGroupUndo : function tabView_waitForGroupUndo(aSpec) { var spec = aSpec || {}; var group = spec.group; var groupObj = null; var self = { reopened: false }; function checkClosed() { self.reopened = true; } if (!group) { throw new Error(arguments.callee.name + ": Group not specified."); } var groupObj = null; this._groupItemsObject.groupItems.forEach(function(node) { if (node.container == group.getNode()) { groupObj = node; } }); if (!groupObj) { throw new Error(arguments.callee.name + ": Group not found."); } try { groupObj.addSubscriber(groupObj, "groupShown", checkClosed); mozmill.utils.waitFor(function () { return self.reopened; }, "Tab Group has not been reopened."); } finally { groupObj.removeSubscriber(groupObj, "groupShown"); } }, /////////////////////////////// // Tabs section /////////////////////////////// /** * Returns the tabs which match the filter criteria * * @param {object} aSpec * Information about the filter to apply * Elements: filter - Type of filter to apply * (active, title) * [optional - default: ""] * value - Value of the element * [optional - default: ""] * * @returns List of tabs * @type {array of ElemBase} */ getTabs : function tabView_getTabs(aSpec) { var spec = aSpec || {}; return this.getElements({ type: "tabs", subtype: spec.filter, value: spec.value }); }, /** * Close a tab * * @param {object} aSpec * Information about the element to operate on * Elements: tab - Tab to close */ closeTab : function tabView_closeTab(aSpec) { var spec = aSpec || {}; var tab = spec.tab; if (!tab) { throw new Error(arguments.callee.name + ": Tab not specified."); } var button = this.getElement({ type: "tab_closeButton", value: tab} ); this._controller.click(button); }, /** * Retrieve the tab's title box * * @param {object} aSpec * Information on which tab to operate on * Elements: tab - Tab * * @returns Tab title box * @type {ElemBase} */ getTabTitleBox : function tabView_getTabTitleBox(aSpec) { var spec = aSpec || {}; var tab = spec.tab; if (!tab) { throw new Error(arguments.callee.name + ": Tab not specified."); } return this.getElement({ type: "tab_titleBox", parent: spec.tab }); }, /** * Open a new tab in the specified group * * @param {object} aSpec * Information about the element to operate on * Elements: group - Group to create a new tab in */ openTab : function tabView_openTab(aSpec) { var spec = aSpec || {}; var group = spec.group; if (!group) { throw new Error(arguments.callee.name + ": Group not specified."); } var button = this.getElement({ type: "group_newTabButton", parent: group }); this._controller.click(button); this.waitForClosed(); }, /////////////////////////////// // UI Elements section /////////////////////////////// /** * Retrieve an UI element based on the given specification * * @param {object} aSpec * Information of the UI elements which should be retrieved * Elements: type - Identifier of the element * subtype - Attribute of the element to filter * [optional - default: ""] * value - Value of the attribute to filter * [optional - default: ""] * parent - Parent of the to find element * [optional - default: document] * * @returns Element which has been found * @type {ElemBase} */ getElement : function tabView_getElement(aSpec) { var elements = this.getElements(aSpec); return (elements.length > 0) ? elements[0] : undefined; }, /** * Retrieve list of UI elements based on the given specification * * @param {object} aSpec * Information of the UI elements which should be retrieved * Elements: type - Identifier of the element * subtype - Attribute of the element to filter * [optional - default: ""] * value - Value of the attribute to filter * [optional - default: ""] * parent - Parent of the to find element * [optional - default: document] * * @returns Elements which have been found * @type {array of ElemBase} */ getElements : function tabView_getElement(aSpec) { var spec = aSpec || { }; var type = spec.type; var subtype = spec.subtype; var value = spec.value; var parent = spec.parent; var root = parent ? parent.getNode() : this._tabViewDoc; var nodeCollector = new domUtils.nodeCollector(root); switch(type) { // Top level elements case "tabView": nodeCollector.root = this._controller.window.document; nodeCollector.queryNodes("#tab-view"); break; case "contentArea": nodeCollector.queryNodes("#content"); break; case "deck": nodeCollector.root = this._controller.window.document; nodeCollector.queryNodes("#tab-view-deck"); break; case "exitButton": nodeCollector.queryNodes("#exit-button"); break; // Group elements case "group_appTabs": nodeCollector.queryNodes(".appTabIcon"); break; case "group_closeButton": nodeCollector.queryNodes(".close"); break; case "group_newTabButton": nodeCollector.queryNodes(".newTabButton"); break; case "group_resizer": nodeCollector.queryNodes(".iq-resizable-handle"); break; case "group_stackExpander": nodeCollector.queryNodes(".stackExpander"); break; case "group_titleBox": nodeCollector.queryNodes(".name"); break; case "group_undoButton": // Bug 596504 - No reference to the undo button nodeCollector.root = this._tabViewDoc; nodeCollector.queryNodes(".undo").filter(function (node) { var groups = this._groupItemsObject.groupItems; for (var i = 0; i < groups.length; i++) { var group = groups[i]; if (group.container == parent.getNode() && group.$undoContainer.length == 1) { return true; } } return false; }, this); break; case "groups": nodeCollector.queryNodes(".groupItem").filter(function (node) { switch(subtype) { case "active": return node.className.indexOf("activeGroup") != -1; case "title": // If no title is given the default name is used if (!value) { value = utils.getProperty("chrome://browser/locale/tabview.properties", "tabview.groupItem.defaultName"); } var title = node.querySelector(".name"); return (value == title.value); default: return true; } }, this); break; // Search elements case "search_box": nodeCollector.queryNodes("#searchbox"); break; case "search_button": nodeCollector.queryNodes("#searchbutton"); break; // Tab elements case "tab_closeButton": nodeCollector.queryNodes(".tab .close"); break; case "tab_favicon": nodeCollector.queryNodes(".tab .favicon"); break; case "tab_titleBox": nodeCollector.queryNodes(".tab .tab-title"); break; case "tabs": nodeCollector.queryNodes(".tab").filter(function (node) { switch (subtype) { case "active": return (node.className.indexOf("focus") != -1); case "group": var group = value ? value.getNode() : null; if (group) { var tabs = this._tabItemsObject.getItems(); for (var i = 0; i < tabs.length; i++) { var tab = tabs[i]; if (tab.parent && tab.parent.container == group) { return true; } } return false; } else { return (node.className.indexOf("tabInGroupItem") == -1); } default: return true; } }, this); break; default: throw new Error(arguments.callee.name + ": Unknown element type - " + aSpec.type); } return nodeCollector.elements; } } // Export of classes exports.tabView = tabView;