Bug 880417 - Create user interface shared module for metro. r=hkupin, r=aeftimie
authorAndreea Matei <andreea.matei@softvision.ro>
Tue, 28 Jan 2014 14:30:00 +0200
changeset 3712 cbd1d6fa1f51dc6cea939ea79ad177b304e60e19
parent 3709 2c85cec7b97a16be5ffaa6a358419b60fa51521c
child 3713 8f1bd29492eb0ac26987a0b3499c32ffdc372df1
push id3425
push userandrei.eftimie@softvision.ro
push dateThu, 06 Feb 2014 09:35:46 +0000
reviewershkupin, aeftimie
bugs880417
Bug 880417 - Create user interface shared module for metro. r=hkupin, r=aeftimie
metrofirefox/lib/tests/manifest.ini
metrofirefox/lib/tests/testStartScreen.js
metrofirefox/lib/tests/testTabs.js
metrofirefox/lib/tests/testToolbar.js
metrofirefox/lib/ui/startScreen.js
metrofirefox/lib/ui/tabs.js
metrofirefox/lib/ui/toolbars.js
new file mode 100644
--- /dev/null
+++ b/metrofirefox/lib/tests/manifest.ini
@@ -0,0 +1,3 @@
+[testStartScreen.js]
+[testTabs.js]
+[testToolbar.js]
new file mode 100644
--- /dev/null
+++ b/metrofirefox/lib/tests/testStartScreen.js
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Include the required modules
+var { expect } = require("../../../lib/assertions");
+var startScreen = require("../ui/startScreen");
+
+const ELEMENTS = [
+  {name: "bookmarks", type: "vbox"},
+  {name: "history", type: "vbox"},
+  {name: "topSites", type: "vbox"}
+];
+
+function setupModule(aModule) {
+  aModule.controller = mozmill.getBrowserController();
+  aModule.startScreen = new startScreen.StartScreen(aModule.controller);
+}
+
+function testStartPage() {
+  controller.open("about:start");
+  controller.waitForPageLoad();
+
+  // Check the containers on the main page
+  ELEMENTS.forEach(function (aElement) {
+    var element = startScreen.getElement({type: aElement.name});
+    expect.equal(element.getNode().localName, aElement.type,
+                 aElement.name + " exists");
+  });
+}
new file mode 100644
--- /dev/null
+++ b/metrofirefox/lib/tests/testTabs.js
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Include the required modules
+var { expect } = require("../../../lib/assertions");
+var tabs = require("../ui/tabs");
+
+const BASE_URL = collector.addHttpResource("../../../data/");
+const TEST_DATA = [
+  BASE_URL + "layout/mozilla_community.html",
+  BASE_URL + "layout/mozilla_mission.html"
+];
+
+const ELEMENTS = [
+  {name: "newTabButton", type: "toolbarbutton"},
+  {name: "sidebar_backButton", type: "div"},
+  {name: "sidebar_newTabButton", type: "div"},
+  {name: "tray", type: "vbox"}
+];
+
+function setupModule(aModule) {
+  aModule.controller = mozmill.getBrowserController();
+  aModule.tabBrowser = new tabs.TabBrowser(aModule.controller);
+}
+
+function teardownModule(aModule) {
+  tabBrowser.closeAllTabs();
+}
+
+function testTabs() {
+  TEST_DATA.forEach(function (aPage) {
+    controller.open(aPage);
+    controller.waitForPageLoad();
+    tabBrowser.openTab();
+  });
+
+  expect.ok(tabBrowser.isVisible(), "Tabs container is visible");
+
+  // Check the elements on each side and the new tab button
+  ELEMENTS.forEach(function (aElement) {
+    var element = tabBrowser.getElement({type: aElement.name});
+    expect.equal(element.getNode().localName, aElement.type,
+                 aElement.name + " exists");
+  });
+
+  tabBrowser.closeTab();
+  expect.equal(tabBrowser.length, 2, "Tab has been closed");
+
+  tabBrowser.openTab();
+  expect.equal(tabBrowser.length, 3, "Another tab has been opened");
+
+  tabBrowser.selectedIndex = 1;
+  expect.equal(tabBrowser.selectedIndex, 1, "Second tab has been selected");
+
+  // Close the first tab
+  tabBrowser.closeTab("button", 0);
+  expect.ok(tabBrowser.selectedIndex === 0 &&
+            controller.tabs.activeTab.location.href.indexOf("mozilla_community") == -1,
+            "First tab has been closed");
+
+  tabBrowser.closeAllTabs();
+  expect.equal(tabBrowser.length, 1, "All previously opened tabs have been closed");
+}
new file mode 100644
--- /dev/null
+++ b/metrofirefox/lib/tests/testToolbar.js
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Include the required modules
+var { expect } = require("../../../lib/assertions");
+var toolbars = require("../ui/toolbars");
+
+const BASE_URL = collector.addHttpResource("../../../data/");
+const TEST_DATA = [
+  BASE_URL + "layout/mozilla_projects.html",
+  BASE_URL + "layout/mozilla_mission.html"
+];
+
+const ELEMENTS = {
+  downloads: [
+    {name: "progressBar", type: "circularprogressindicator"}
+  ],
+  findBar: [
+    {name: "findbar", type:"appbar"},
+    {name: "nextButton", type: "toolbarbutton"},
+    {name: "previousButton", type: "toolbarbutton"}
+  ],
+  locationBar: [
+    {name: "backButton", type: "toolbarbutton"},
+    {name: "forwardButton", type: "toolbarbutton"},
+    {name: "reloadButton", type: "toolbarbutton"},
+    {name: "stopButton", type: "toolbarbutton"}
+  ],
+  toolBar: [
+    {name: "findInPage", type: "richlistitem"},
+    {name: "menuButton", type: "toolbarbutton"},
+    {name: "pinButton", type: "toolbarbutton"},
+    {name: "starButton", type: "toolbarbutton"}
+  ]
+};
+
+function setupModule(aModule) {
+  aModule.controller = mozmill.getBrowserController();
+  aModule.toolbar = new toolbars.ToolBar(aModule.controller);
+}
+
+function testDownloads() {
+  controller.open("about:support");
+  controller.waitForPageLoad();
+
+  ELEMENTS.downloads.forEach(function (aElement) {
+    var element = toolbar.downloads.getElement({type: aElement.name});
+    expect.equal(element.getNode().localName, aElement.type, aElement.name + " exists");
+  });
+}
+
+function testFindBar() {
+  controller.open(TEST_DATA[0]);
+  controller.waitForPageLoad();
+
+  // Open findbar via menu button and check it's elements
+  toolbar.findBar.open("menu");
+  expect.ok(toolbar.findBar.isOpen, "Findbar has been opened via menu");
+
+  ELEMENTS.findBar.forEach(function (aElement) {
+    var element = toolbar.findBar.getElement({type: aElement.name});
+    expect.equal(element.getNode().localName, aElement.type, aElement.name + " exists");
+  });
+
+  var textbox = toolbar.findBar.getElement({type: "textbox"});
+  toolbar.findBar.type("project");
+  expect.equal(textbox.getNode().value, "project", "Expected text is present");
+
+  // Close findbar via the close button
+  toolbar.findBar.close();
+  expect.ok(!toolbar.findBar.isOpen, "Findbar has been closed via the close button");
+
+  // Open and close findbar via shortcut
+  toolbar.findBar.open("shortcut");
+  expect.ok(toolbar.findBar.isOpen, "Findbar has been opened via shortcut");
+
+  toolbar.findBar.close("shortcut");
+  expect.ok(!toolbar.findBar.isOpen, "Findbar has been closed via shortcut");
+}
+
+function testToolbarCheck() {
+  toolbar.open();
+  expect.ok(toolbar.isVisible(), "Toolbar has been opened");
+
+  toolbar.locationBar.focus();
+  toolbar.locationBar.type("url");
+  expect.equal(toolbar.locationBar.value, "url", "Correct text has been typed in location bar");
+
+  var closeSuggestionsButton = toolbar.getElement({type: "closeSuggestionsButton"});
+  closeSuggestionsButton.tap();
+
+  var suggestionsPanel = toolbar.getElement({type: "suggestionsPanel"});
+  expect.ok(suggestionsPanel.getNode().hidden, "Suggestions panel has been closed");
+
+  toolbar.locationBar.clear();
+  expect.equal(toolbar.locationBar.value, "", "Location bar is empty");
+
+  TEST_DATA.forEach(function (aPage) {
+    controller.open(aPage);
+    controller.waitForPageLoad();
+  });
+
+  // Check the buttons of the toolBar and the locationBar
+  ELEMENTS.toolBar.forEach(function (aElement) {
+    var element = toolbar.getElement({type: aElement.name});
+    expect.equal(element.getNode().localName, aElement.type, aElement.name + " exists");
+  });
+
+  ELEMENTS.locationBar.forEach(function (aElement) {
+    var element = toolbar.locationBar.getElement({type: aElement.name});
+    expect.equal(element.getNode().localName, aElement.type, aElement.name + " exists");
+  });
+}
new file mode 100644
--- /dev/null
+++ b/metrofirefox/lib/ui/startScreen.js
@@ -0,0 +1,197 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Constructor
+ *
+ * @param {MozMillController} aController
+ *        Controller of the window
+ */
+function Appbar(aController) {
+  if (!aController) {
+    assert.fail("A valid controller must be specified");
+  }
+
+  this._controller = aController;
+}
+
+/**
+ * Prototype definition of the Appbar class
+ */
+Appbar.prototype = {
+  /**
+   * Returns the controller of the class instance
+   *
+   * @returns {MozMillController} Controller of the window
+   */
+  get controller() {
+    return this._controller;
+  },
+
+  /**
+   * Retrieve an UI element based on the given specification
+   *
+   * @param {object} aSpec
+   *        Information of the UI element which should be retrieved
+   * @param {object} type
+   *        Identifier of the element
+   * @param {object} [subtype=""]
+   *        Attribute of the element to filter
+   * @param {object} [value=""]
+   *        Value of the attribute to filter
+   * @param {object} [parent=document]
+   *        Parent of the element to find
+   *
+   * @returns {object} Element which has been found
+   */
+  getElement : function appbar_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
+   * @param {Object} type
+   *        General type information
+   * @param {Object} subtype
+   *        Specific element or property
+   * @param {Object} value
+   *        Value of the element or property
+   *
+   * @returns {Object[]} Array of elements which have been found
+   */
+  getElements : function appbar_getElements(aSpec) {
+    var elem = null;
+
+    switch(aSpec.type) {
+      case "clearButton":
+        elem = new findElement.ID(this._controller.window.document, "clear-selected-button");
+        break;
+      case "deleteButton":
+        elem = new findElement.ID(this._controller.window.document, "delete-selected-button");
+        break;
+      case "hideButton":
+        elem = new findElement.ID(this._controller.window.document, "hide-selected-button");
+        break;
+      case "itemsAppbar":
+        elem = new findElement.ID(this._controller.window.document, "contextappbar");
+        break;
+      case "itemsActions":
+        elem = new findElement.ID(this._controller.window.document, "contextualactions-tray");
+        break;
+      case "pinToStartButton":
+        elem = new findElement.ID(this._controller.window.document, "pin-selected-button");
+        break;
+      case "restoreButton":
+        elem = new findElement.ID(this._controller.window.document, "restore-selected-button");
+        break;
+      case "unpinButton":
+        elem = new findElement.ID(this._controller.window.document, "unpin-selected-button");
+        break;
+      default:
+        throw new Error("Unknown element type - " + aSpec.type);
+    }
+
+    return [elem];
+  }
+};
+
+/**
+ * Constructor for the start page which has Top Sites, Bookmarks and History
+ *
+ * @param {MozMillController} aController
+ *        Controller of the window
+ */
+function StartScreen(aController) {
+  if (!aController) {
+    assert.fail("A valid controller must be specified");
+  }
+
+  this._controller = aController;
+  this.appbar = new Appbar(aController);
+}
+
+/**
+ * Prototype definition of the Metro mode class
+ */
+StartScreen.prototype = {
+  /**
+   * Returns the controller of the class instance
+   *
+   * @returns {MozMillController} Controller of the window
+   */
+  get controller() {
+    return this._controller;
+  },
+
+  /**
+   * Retrieve an UI element based on the given specification
+   *
+   * @param {object} aSpec
+   *        Information of the UI element which should be retrieved
+   * @param {object} type
+   *        Identifier of the element
+   * @param {object} [subtype=""]
+   *        Attribute of the element to filter
+   * @param {object} [value=""]
+   *        Value of the attribute to filter
+   * @param {object} [parent=document]
+   *        Parent of the element to find
+   *
+   * @returns {object} Element which has been found
+   */
+  getElement : function screen_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
+   * @param {Object} type
+   *        General type information
+   * @param {Object} subtype
+   *        Specific element or property
+   * @param {Object} value
+   *        Value of the element or property
+   *
+   * @returns {Object[]} Array of elements which have been found
+   */
+  getElements : function screen_getElements(aSpec) {
+    var elem = null;
+
+    switch(aSpec.type) {
+      case "bookmarks":
+        elem = new findElement.ID(this._controller.window.document, "start-bookmarks");
+        break;
+      case "history":
+        elem = new findElement.ID(this._controller.window.document, "start-history");
+        break;
+      case "remoteTabs":
+        elem = new findElement.ID(this._controller.window.document, "start-remotetabs");
+        break;
+      case "startContainer":
+        elem = new findElement.ID(this._controller.window.document, "start-container");
+        break;
+      case "topSites":
+        elem = new findElement.ID(this._controller.window.document, "start-topsites");
+        break;
+      default:
+        throw new Error("Unknown element type - " + aSpec.type);
+    }
+
+    return [elem];
+  }
+};
+
+exports.StartScreen = StartScreen;
new file mode 100644
--- /dev/null
+++ b/metrofirefox/lib/ui/tabs.js
@@ -0,0 +1,320 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+var { assert } = require("../../../lib/assertions");
+var domUtils = require("../../../lib/dom-utils");
+var utils = require("../../../firefox/lib/utils");
+
+/**
+ * Constructor
+ *
+ * @param {MozMillController} aController
+ *        Controller of the window
+ */
+function TabBrowser(aController) {
+  if (!aController) {
+    assert.fail("A valid controller must be specified");
+  }
+
+  this._controller = aController;
+}
+
+/**
+ * Prototype definition of the TabBrowser class
+ */
+TabBrowser.prototype = {
+  /**
+   * Returns the controller of the class instance
+   *
+   * @returns {MozMillController} Controller of the window
+   */
+  get controller() {
+    return this._controller;
+  },
+
+  /**
+   * Gets all the needed external DTD URLs as an array
+   *
+   * @returns {String[]} URL's of external DTD files
+   */
+  get dtds() {
+    var dtds = ["chrome://browser/locale/browser.dtd"];
+
+    return dtds;
+  },
+
+  /**
+   * Returns the number of open tabs
+   *
+   * @returns {Number} Number of open tabs
+   */
+  get length() {
+    // Bug 968079
+    // TODO: Update the code to use UI elements
+    return this._controller.tabs.length;
+  },
+
+  /**
+   * Returns the index of the active tab
+   *
+   * @returns {Number} Index of the active tab
+   */
+  get selectedIndex() {
+    // Bug 968079
+    // TODO: Update the code to use UI elements
+    return this._controller.tabs.activeTabIndex;
+  },
+
+  /**
+   * Returns the tab at the selected index
+   *
+   * @returns {ElemBase} Tab at the selected index
+   */
+  set selectedIndex(aIndex) {
+    // Bug 968079
+    // TODO: Update the code to use UI elements
+    return this._controller.tabs.selectTabIndex(aIndex);
+  },
+
+  /**
+   * Retrieve an UI element based on the given specification
+   *
+   * @param {object} aSpec
+   *        Information of the UI element which should be retrieved
+   * @param {object} type
+   *        Identifier of the element
+   * @param {object} [subtype=""]
+   *        Attribute of the element to filter
+   * @param {object} [value=""]
+   *        Value of the attribute to filter
+   * @param {object} [parent=document]
+   *        Parent of the element to find
+   *
+   * @returns {object} Element which has been found
+   */
+  getElement : function tabBrowser_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
+   * @param {Object} type
+   *        General type information
+   * @param {Object} subtype
+   *        Specific element or property
+   * @param {Object} value
+   *        Value of the element or property
+   *
+   * @returns {Object[]} Array of elements which have been found
+   */
+  getElements : function tabBrowser_getElements(aSpec) {
+    var elem = null;
+    var document = this._controller.window.document;
+
+    switch(aSpec.type) {
+      case "closeButton":
+        var node = document.getAnonymousElementByAttribute(this.getTab(),
+                                                           "anonid", "close");
+        elem = new findElement.Elem(node);
+        break;
+      case "newTabButton":
+        elem = new findElement.ID(document, "newtab-button");
+        break;
+      case "sidebar_backButton":
+        elem = new findElement.ID(document, "overlay-back");
+        break;
+      case "sidebar_newTabButton":
+        elem = new findElement.ID(document, "overlay-plus");
+        break;
+      case "tabsContainer":
+        elem = new findElement.ID(document, "tabs-container");
+        break;
+      case "tabs":
+        // Bug 968079
+        // TODO: Update the code to use UI elements instead of strip
+        var tabs = new findElement.ID(document, "tabs");
+        var root = tabs.getNode().strip;
+        var nodeCollector = new domUtils.nodeCollector(root);
+        elem = nodeCollector.queryNodes("documenttab");
+        break;
+      case "tray":
+        elem = new findElement.ID(document, "tray");
+        break;
+      default:
+        throw new Error("Unknown element type - " + aSpec.type);
+    }
+
+    return [elem];
+  },
+
+  /**
+   * Get the tab at the specified index
+   *
+   * @param {number} aIndex
+   *        Index of the tab
+   *
+   * @returns {ElemBase} The requested tab
+   */
+  getTab : function tabBrowser_getTab(aIndex) {
+    if (aIndex === undefined)
+      aIndex = this.selectedIndex;
+
+    var tabsList = this.getElement({type: "tabs"});
+    return tabsList.nodes[aIndex];
+  },
+
+  /**
+   * Check if the tabs container at the top of the browser is visible
+   *
+   * @returns {Boolean} True if the tabs container is visible
+   */
+  isVisible: function tabBrowser_isVisible() {
+    var tabs = this.getElement({type: "tray"});
+
+    return tabs.getNode().hasAttribute("expanded");
+  },
+
+  /**
+   * Open the tabs container at the top of the browser, which contains all current tabs
+   *
+   * @param {string} aMethod
+   *        Specifies a method for opening the tabs container
+   */
+  openContainer: function tabBrowser_openContainer(aMethod) {
+    var method = aMethod || "shortcut";
+
+    switch (method) {
+      case "shortcut":
+        // Bug 964704
+        // TODO: add code for shortcuts, if any
+        break;
+      case "touchEvent":
+        // Bug 964704
+        // TODO: add code for swipe up
+        break;
+      default:
+        throw new Error("Unknown opening method - " + method);
+    }
+
+    var self = this;
+    assert.waitFor(function () {
+      return self.isVisible();
+    }, "Tabs container is visible");
+  },
+
+  /**
+   * Open a new tab
+   *
+   * @param {String} [aEventType="shortcut"]
+   *        Type of event which triggers the action
+   *
+   */
+  openTab : function tabBrowser_openTab(aEventType) {
+    var type = aEventType || "shortcut";
+
+    // Add event listener to wait until the tab has been opened
+    var self = { opened: false };
+    function checkTabOpened() { self.opened = true; }
+    this._controller.window.addEventListener("TabOpen", checkTabOpened, false);
+
+    try {
+      switch (type) {
+        case "newTabButton":
+          var newTabButton = this.getElement({type: "newTabButton"});
+          newTabButton.tap();
+          break;
+        case "shortcut":
+          var win = new findElement.MozMillElement("Elem", this._controller.window);
+          var cmdKey = utils.getEntity(this.dtds, "newTab.key");
+          win.keypress(cmdKey, {accelKey: true});
+          break;
+        case "shortcut2":
+          var win = new findElement.MozMillElement("Elem", this._controller.window);
+          var cmdKey = utils.getEntity(this.dtds, "newTab2.key");
+          win.keypress(cmdKey, {accelKey: true});
+          break;
+        case "sidebarButton":
+          var sideNewTab = this.getElement({type: "sidebar_newTabButton"});
+          sideNewTab.tap();
+          break;
+        default:
+          throw new Error("Unknown event type - " + type);
+      }
+
+      assert.waitFor(function () {
+        return self.opened;
+      }, "New tab has been opened");
+    }
+    finally {
+      this._controller.window.removeEventListener("TabOpen", checkTabOpened, false);
+    }
+  },
+
+  /**
+   * Close all tabs of the window and open a blank tab
+   */
+  closeAllTabs : function tabBrowser_closeAllTabs() {
+    while (this.length > 1) {
+      this.closeTab();
+    }
+
+    this._controller.open(this._controller.window.BROWSER_NEW_TAB_URL);
+    this._controller.waitForPageLoad();
+  },
+
+  /**
+   * Close an open tab
+   *
+   * @param {String} [aEventType="shortcut"]
+   *        Type of event which triggers the action
+   * @param {Number} [aIndex=selectedIndex]
+   *        Index of the tab to close
+   */
+  closeTab : function tabBrowser_closeTab(aEventType, aIndex) {
+    var type = aEventType || "shortcut";
+
+    // Add event listener to wait until the tab has been closed
+    var self = { closed: false };
+    function checkTabClosed() { self.closed = true; }
+    this._controller.window.addEventListener("TabClose", checkTabClosed, false);
+
+    if (aIndex !== undefined) {
+      this.selectedIndex = aIndex;
+    }
+
+    try {
+      switch (type) {
+        case "button":
+          var closeButton = this.getElement({type: "closeButton", value: this.getTab()});
+          closeButton.click();
+          break;
+        case "shortcut":
+          var win = new findElement.MozMillElement("Elem", this._controller.window);
+          var cmdKey = utils.getEntity(this.dtds, "closeTab.key");
+          win.keypress(cmdKey, {accelKey: true});
+          break;
+        default:
+          throw new Error("Unknown event type - " + type);
+      }
+
+      assert.waitFor(function () {
+        return self.closed;
+      }, "Tab has been closed");
+    }
+    finally {
+      this._controller.window.removeEventListener("TabClose", checkTabClosed, false);
+    }
+  }
+};
+
+exports.TabBrowser = TabBrowser;
new file mode 100644
--- /dev/null
+++ b/metrofirefox/lib/ui/toolbars.js
@@ -0,0 +1,623 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var { assert } = require("../../../lib/assertions");
+var utils = require("../../../firefox/lib/utils");
+var tabs = require("tabs");
+
+/**
+ * Constructor
+ *
+ * @param {Object} aToolBar
+ *        Instance of the ToolBar class
+ */
+function Downloads(aToolBar) {
+  this.toolbar = aToolBar;
+  this._controller = aToolBar.controller;
+}
+
+/**
+ * Prototype definition of the Downloads class
+ */
+Downloads.prototype = {
+  /**
+   * Returns the controller of the class instance
+   *
+   * @returns {MozMillController} Controller of the window
+   */
+  get controller() {
+    return this._controller;
+  },
+
+  /**
+   * Retrieve an UI element based on the given specification
+   *
+   * @param {object} aSpec
+   *        Information of the UI elements which should be retrieved
+   * @param {object} type
+   *        Identifier of the element
+   * @param {object} [subtype=""]
+   *        Attribute of the element to filter
+   * @param {object} [value=""]
+   *        Value of the attribute to filter
+   * @param {object} [parent=document]
+   *        Parent of the to find element
+   *
+   * @returns {Object} Element which has been found
+   */
+  getElement : function downloads_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
+   * @param {Object} type
+   *        General type information
+   * @param {Object} subtype
+   *        Specific element or property
+   * @param {Object} value
+   *        Value of the element or property
+   *
+   * @returns {Object[]} Array of elements which have been found
+   */
+  getElements : function downloads_getElements(aSpec) {
+    var elem = null;
+
+    switch(aSpec.type) {
+      case "progressBar":
+        elem = new findElement.ID(this._controller.window.document, "download-progress");
+        break;
+      default:
+        throw new Error("Unknown element type - " + aSpec.type);
+    }
+
+    return [elem];
+  }
+};
+
+/**
+ * Constructor
+ *
+ * @param {Object} aToolBar
+ *        Instance of the ToolBar class
+ */
+function FindBar(aToolBar) {
+  this.toolbar = aToolBar;
+  this._controller = aToolBar.controller;
+}
+
+/**
+ * Prototype definition of the Find bar class
+ */
+FindBar.prototype = {
+  /**
+   * Returns the controller of the class instance
+   *
+   * @returns {MozMillController} Controller of the window
+   */
+  get controller() {
+    return this._controller;
+  },
+
+  /**
+   * Returns the state of the find bar (opened/closed)
+   */
+  get isOpen() {
+    var findbar = this.getElement({type: "findbar"});
+    return findbar.getNode().isShowing;
+  },
+
+  /**
+   * Returns the current value in the find bar
+   */
+  get value() {
+    var textbox = this.getElement({type: "textbox"});
+    return textbox.getNode().value;
+   },
+
+  /**
+   * Retrieve an UI element based on the given specification
+   *
+   * @param {object} aSpec
+   *        Information of the UI element which should be retrieved
+   * @param {object} type
+   *        Identifier of the element
+   * @param {object} [subtype=""]
+   *        Attribute of the element to filter
+   * @param {object} [value=""]
+   *        Value of the attribute to filter
+   * @param {object} [parent=document]
+   *        Parent of the element to find
+   *
+   * @returns {object} Element which has been found
+   */
+  getElement : function findbar_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
+   * @param {Object} type
+   *        General type information
+   * @param {Object} subtype
+   *        Specific element or property
+   * @param {Object} value
+   *        Value of the element or property
+   *
+   * @returns {Object[]} Array of elements which have been found
+   */
+  getElements : function findbar_getElements(aSpec) {
+    var elem = null;
+
+    switch(aSpec.type) {
+      case "findbar":
+        elem = new findElement.ID(this._controller.window.document, "findbar");
+        break;
+      case "closeButton":
+        elem = new findElement.ID(this._controller.window.document, "findbar-close-button");
+        break;
+      case "nextButton":
+        elem = new findElement.ID(this._controller.window.document, "findbar-next-button");
+        break;
+      case "previousButton":
+        elem = new findElement.ID(this._controller.window.document, "findbar-previous-button");
+        break;
+      case "textbox":
+        elem = new findElement.ID(this._controller.window.document, "findbar-textbox");
+        break;
+      default:
+        throw new Error("Unknown element type - " + aSpec.type);
+    }
+
+    return [elem];
+  },
+
+  /**
+   * Close the find bar
+   *
+   * @param {string} aMethod
+   *        Specifies a method for closing the find bar
+   */
+  close: function FindBar_close(aMethod) {
+    var method = aMethod || "button";
+    var transitioned = false;
+
+    function onTransitionEnd() {
+      transitioned = true;
+    }
+
+    var findBar = this.getElement({type: "findbar"});
+    findBar.getNode().addEventListener("transitionend", onTransitionEnd, false);
+
+    try {
+      switch (method) {
+        case "button":
+          var closeButton = this.getElement({type: "closeButton"});
+          closeButton.tap();
+          break;
+        case "shortcut":
+          var win = new findElement.MozMillElement("Elem", this._controller.window);
+          win.keypress("VK_ESCAPE", {accelKey: true});
+          break;
+        default:
+          throw new Error("Unknown opening method - " + method);
+      }
+
+      assert.waitFor(function () {
+        return transitioned;
+      }, "Find bar has been closed");
+    }
+    finally {
+      findBar.getNode().removeEventListener("transitionend", onTransitionEnd, false);
+    }
+  },
+
+  /**
+   * Open the find bar
+   *
+   * @param {string} aMethod
+   *        Specifies a method for opening the find bar
+   */
+  open: function FindBar_open(aMethod) {
+    var method = aMethod || "menu";
+    var transitioned = false;
+
+    function onTransitionEnd() {
+      transitioned = true;
+    }
+
+    var findBar = this.getElement({type: "findbar"});
+    findBar.getNode().addEventListener("transitionend", onTransitionEnd, false);
+
+    try {
+      switch (method) {
+        case "menu":
+          // In order to tap on the menu button, the toolbar has to be opened
+          if (!this.toolbar.isVisible()) {
+            this.toolbar.open();
+          }
+
+          var menuButton = this.toolbar.getElement({type: "menuButton"});
+          menuButton.tap();
+
+          // Bug 966963
+          // TODO: Update code to use controller.Menu
+          var findInPage = this.toolbar.getElement({type: "findInPage"});
+          findInPage.tap();
+          break;
+        case "shortcut":
+          var win = new findElement.MozMillElement("Elem", this._controller.window);
+          var cmdKey = utils.getEntity(this.toolbar.dtds, "find.key");
+          win.keypress(cmdKey, {accelKey: true});
+          break;
+        default:
+          throw new Error("Unknown opening method - " + method);
+      }
+
+      assert.waitFor(function () {
+        return transitioned;
+      }, "Find bar has been opened");
+    }
+    finally {
+      findBar.getNode().removeEventListener("transitionend", onTransitionEnd, false);
+    }
+  },
+
+  /**
+   * Type a search term into the find bar
+   *
+   * @param {string} aSearchTerm
+   *        String term to search for in the find bar
+   */
+  type : function FindBar_type(aSearchTerm) {
+    var textbox = this.getElement({type: "textbox"});
+    textbox.sendKeys(aSearchTerm);
+  }
+};
+
+/**
+ * Constructor
+ *
+ * @param {Object} aToolBar
+ *        Instance of the ToolBar class
+ */
+function LocationBar(aToolBar) {
+  this.toolbar = aToolBar;
+  this._controller = aToolBar.controller;
+}
+
+/**
+ * Location Bar class
+ */
+LocationBar.prototype = {
+  /**
+   * Returns the controller of the class instance
+   *
+   * @returns {MozMillController} Controller of the window
+   */
+  get controller() {
+    return this._controller;
+  },
+
+  /**
+   * Returns the urlbar element
+   *
+   * @returns URL bar
+   * @type {ElemBase}
+   */
+  get urlbar() {
+    return this.getElement({type: "urlbar"});
+  },
+
+  /**
+   * Returns the currently shown URL
+   *
+   * @returns Text inside the location bar
+   * @type {String}
+   */
+  get value() {
+    return this.urlbar.getNode().value;
+  },
+
+  /**
+   * Clear the location bar
+   */
+  clear : function locationBar_clear() {
+    this.focus();
+    this.urlbar.keypress("VK_DELETE");
+    var self = this;
+    assert.waitFor(function () {
+      return self.urlbar.getNode().value === "";
+    }, "Location bar has been cleared.");
+  },
+
+  /**
+   * Check if the location bar contains the given text
+   *
+   * @param {String} text
+   *        Text which should be checked against
+   */
+  contains : function locationBar_contains(aText) {
+    return this.value.indexOf(aText) !== -1;
+  },
+
+  /**
+   * Focus the location bar
+   */
+  focus : function locationBar_focus() {
+    var self = this;
+    this.urlbar.tap();
+    assert.waitFor(function () {
+      return self.urlbar.getNode().getAttribute("focused") === "true";
+    }, "Location bar has been focused");
+  },
+
+  /**
+   * Retrieve an UI element based on the given specification
+   *
+   * @param {object} aSpec
+   *        Information of the UI element which should be retrieved
+   * @param {object} type
+   *        Identifier of the element
+   * @param {object} [subtype=""]
+   *        Attribute of the element to filter
+   * @param {object} [value=""]
+   *        Value of the attribute to filter
+   * @param {object} [parent=document]
+   *        Parent of the element to find
+   *
+   * @returns {object} Element which has been found
+   */
+  getElement : function locationBar_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
+   * @param {Object} type
+   *        General type information
+   * @param {Object} subtype
+   *        Specific element or property
+   * @param {Object} value
+   *        Value of the element or property
+   *
+   * @returns {Object[]} Array of elements which have been found
+   */
+  getElements : function locationBar_getElements(aSpec) {
+    var elem = null;
+
+    switch(aSpec.type) {
+      case "backButton":
+        elem = new findElement.ID(this._controller.window.document, "back-button");
+        break;
+      case "forwardButton":
+        elem = new findElement.ID(this._controller.window.document, "forward-button");
+        break;
+      case "identityBox":
+        elem = new findElement.ID(this._controller.window.document, "identity-box");
+        break;
+      case "reloadButton":
+        elem = new findElement.ID(this._controller.window.document, "reload-button");
+        break;
+      case "stopButton":
+        elem = new findElement.ID(this._controller.window.document, "stop-button");
+        break;
+      case "urlbar":
+        elem = new findElement.ID(this.controller.window.document, "urlbar-edit");
+        break;
+      default:
+        throw new Error("Unknown element type - " + aSpec.type);
+    }
+
+    return [elem];
+  },
+
+  /**
+   * Type the given text into the location bar
+   *
+   * @param {string} aText
+   *        Text to enter into the location bar
+   */
+  type : function locationBar_type(aText) {
+    var location = this.getElement({type: "urlbar"});
+    location.sendKeys(aText);
+  }
+
+};
+
+/**
+ * Constructor
+ */
+function ToolBar(aController) {
+  if (!aController) {
+    assert.fail("A valid controller must be specified");
+  }
+
+  this._controller = aController;
+  this.downloads = new Downloads(this);
+  this.findBar = new FindBar(this);
+  this.locationBar = new LocationBar(this);
+}
+
+/**
+ * Prototype definition of the tool bar class
+ */
+ToolBar.prototype = {
+  /**
+   * Returns the controller of the class instance
+   *
+   * @returns {MozMillController} Controller of the window
+   */
+  get controller() {
+    return this._controller;
+  },
+
+  /**
+   * Gets all the needed external DTD URLs as an array
+   *
+   * @returns {String[]} URL's of external DTD files
+   */
+  get dtds() {
+    var dtds = ["chrome://browser/locale/browser.dtd"];
+
+    return dtds;
+  },
+
+  /**
+   * Retrieve an UI element based on the given specification
+   *
+   * @param {object} aSpec
+   *        Information of the UI elements which should be retrieved
+   * @param {object} type
+   *        Identifier of the element
+   * @param {object} [subtype=""]
+   *        Attribute of the element to filter
+   * @param {object} [value=""]
+   *        Value of the attribute to filter
+   * @param {object} [parent=document]
+   *        Parent of the to find element
+   *
+   * @returns {object} Element which has been found
+   */
+  getElement : function toolbar_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
+   * @param {Object} type
+   *        General type information
+   * @param {Object} subtype
+   *        Specific element or property
+   * @param {Object} value
+   *        Value of the element or property
+   *
+   * @returns {Object[]} Array of elements which have been found
+   */
+  getElements : function toolbar_getElements(aSpec) {
+    var elem = null;
+
+    switch(aSpec.type) {
+      case "closeSuggestionsButton":
+        elem = new findElement.ID(this._controller.window.document, "panel-close-button");
+        break;
+      case "findInPage":
+        elem = new findElement.ID(this._controller.window.document, "context-findinpage");
+        break;
+      case "menuButton":
+        elem = new findElement.ID(this._controller.window.document, "menu-button");
+        break;
+      case "navbar":
+        elem = new findElement.ID(this._controller.window.document, "navbar");
+        break;
+      case "pinButton":
+        elem = new findElement.ID(this._controller.window.document, "pin-button");
+        break;
+      case "starButton":
+        elem = new findElement.ID(this._controller.window.document, "star-button");
+        break;
+      case "suggestionsPanel":
+        elem = new findElement.ID(this._controller.window.document, "panel-container");
+        break;
+      case "toolbar":
+        elem = new findElement.ID(this._controller.window.document, "toolbar");
+        break;
+      case "toolbarClose":
+        elem = new findElement.ID(this._controller.window.document, "close-button");
+        break;
+      default:
+        throw new Error("Unknown element type - " + aSpec.type);
+    }
+
+    return [elem];
+  },
+
+  /**
+   * Bookmarks the current page
+   */
+  bookmarkPage: function toolbar_bookmarkPage() {
+    var starButton = this.getElement({type: "starButton"});
+    starButton.tap();
+    assert.waitFor(function () {
+      return starButton.getNode().checked;
+    }, "Page has been bookmarked");
+  },
+
+  /**
+   * Check if the toolbar is visible
+   *
+   * @returns {Boolean} True if the toolbar is visible
+   */
+  isVisible: function toolbar_isVisible() {
+    var toolbar = this.getElement({type: "navbar"});
+    return toolbar.getNode().hasAttribute("visible") ||
+           toolbar.getNode().hasAttribute("startpage");
+  },
+
+  /**
+   * Open the toolbar
+   *
+   * @param {string} aMethod
+   *        Specifies a method for opening the toolbar
+   */
+  open: function toolbar_open(aMethod) {
+    var method = aMethod || "shortcut";
+    var transitioned = false;
+
+    function onTransitionEnd() {
+      transitioned = true;
+    }
+
+    var toolbar = this.getElement({type: "toolbar"});
+    toolbar.getNode().addEventListener("transitionend", onTransitionEnd, false);
+
+    try {
+      switch (method) {
+        case "shortcut":
+          var win = new findElement.MozMillElement("Elem", this._controller.window);
+          var cmdKey = utils.getEntity(this.dtds, "urlbar.accesskey");
+          win.keypress(cmdKey, {accelKey:true});
+          break;
+        case "touchEvent":
+          // Bug 964704
+          // TODO: add code for touch event (swipe up)
+          break;
+        default:
+          throw new Error("Unknown opening method - " + method);
+      }
+
+      assert.waitFor(function () {
+        return transitioned;
+      }, "Toolbar has been opened");
+    }
+    finally {
+      toolbar.getNode().removeEventListener("transitionend", onTransitionEnd, false);
+    }
+  }
+};
+
+// Export of classes
+exports.ToolBar = ToolBar;