function test() {
  waitForExplicitFinish();

  gBrowser.addTab();
  gBrowser.addTab();
  gBrowser.addTab();

  assertTabs(4);

  ctrlTabTest([2]      , 1, 0);
  ctrlTabTest([2, 3, 1], 2, 2);
  ctrlTabTest([]       , 4, 2);

  {
    let selectedIndex = gBrowser.tabContainer.selectedIndex;
    pressCtrlTab();
    pressCtrlTab(true);
    releaseCtrl();
    is(gBrowser.tabContainer.selectedIndex, selectedIndex,
       "Ctrl+Tab -> Ctrl+Shift+Tab keeps the selected tab");
  }

  { // test for bug 445369
    let tabs = gBrowser.tabs.length;
    pressCtrlTab();
    EventUtils.synthesizeKey("w", { ctrlKey: true });
    is(gBrowser.tabs.length, tabs - 1, "Ctrl+Tab -> Ctrl+W removes one tab");
    releaseCtrl();
  }
  assertTabs(3);

  ctrlTabTest([2, 1, 0], 7, 1);

  { // test for bug 445369
    selectTabs([1, 2, 0]);

    let selectedTab = gBrowser.selectedTab;
    let tabToRemove = gBrowser.tabs[1];

    pressCtrlTab();
    pressCtrlTab();
    EventUtils.synthesizeKey("w", { ctrlKey: true });
    ok(!tabToRemove.parentNode,
       "Ctrl+Tab*2 -> Ctrl+W removes the second most recently selected tab");

    pressCtrlTab(true);
    pressCtrlTab(true);
    releaseCtrl();
    ok(gBrowser.selectedTab == selectedTab,
       "Ctrl+Tab*2 -> Ctrl+W -> Ctrl+Shift+Tab*2 keeps the selected tab");
  }
  assertTabs(2);

  ctrlTabTest([1], 1, 0);

  gBrowser.removeTab(gBrowser.tabContainer.lastChild);

  assertTabs(1);

  { // test for bug 445768
    let focusedWindow = document.commandDispatcher.focusedWindow;
    let eventConsumed = true;
    let detectKeyEvent = function (event) {
      eventConsumed = event.getPreventDefault();
    };
    document.addEventListener("keypress", detectKeyEvent, false);
    pressCtrlTab();
    document.removeEventListener("keypress", detectKeyEvent, false);
    ok(eventConsumed, "Ctrl+Tab consumed by the tabbed browser if one tab is open");
    is(focusedWindow.location, document.commandDispatcher.focusedWindow.location,
       "Ctrl+Tab doesn't change focus if one tab is open");
  }

  gBrowser.addTab();
  gBrowser.addTab();
  gBrowser.addTab();

  assertTabs(4);
  selectTabs([0, 1, 2, 3]);
  pressCtrlTab();
  ctrlTab.panel.addEventListener("popupshown", stickyTests, false);

  function stickyTests() {
    ctrlTab.panel.removeEventListener("popupshown", stickyTests, false);

    EventUtils.synthesizeKey("f", { ctrlKey: true });
    is(document.activeElement, ctrlTab.searchField.inputField,
       "Ctrl+Tab -> Ctrl+F focuses the panel's search field");

    releaseCtrl();
    ok(isOpen(),
       "panel is sticky after focusing the search field and releasing the Ctrl key");

    EventUtils.synthesizeKey("f", {});
    EventUtils.synthesizeKey("o", {});
    EventUtils.synthesizeKey("o", {});
    is(ctrlTab.searchField.value, "foo",
       "text entered into search field");

    EventUtils.synthesizeKey("VK_RETURN", {});
    ok(isOpen(),
       "Enter key kicks pending search off; the panel stays open as there's no match");
    is(ctrlTab.searchField.value, "foo",
       "search field value persists after Enter pressed");

    EventUtils.synthesizeKey("VK_ESCAPE", {});
    is(ctrlTab.searchField.value, "",
       "ESC key clears the search field");
    ok(isOpen(),
       "Clearing the search field with ESC keeps the panel open");

    // blur the search field
    EventUtils.synthesizeKey("VK_TAB", {});
    isnot(document.activeElement, ctrlTab.searchField.inputField,
          "Tab key blurs the panel's search field");

    // advance selection and close panel
    EventUtils.synthesizeKey("VK_TAB", {});
    EventUtils.synthesizeKey("VK_TAB", {});
    EventUtils.synthesizeKey("VK_RETURN", {});
    ok(!isOpen(),
       "Enter key closes the panel");
    is(gBrowser.tabContainer.selectedIndex, 1,
       "Tab key advances the selection while the panel is sticky");

    gBrowser.removeCurrentTab();
    gBrowser.removeCurrentTab();
    gBrowser.removeCurrentTab();
    assertTabs(1);
    finish();
  }


  /* private utility functions */

  function pressCtrlTab(aShiftKey)
    EventUtils.synthesizeKey("VK_TAB", { ctrlKey: true, shiftKey: !!aShiftKey });

  function releaseCtrl()
    EventUtils.synthesizeKey("VK_CONTROL", { type: "keyup" });

  function isOpen()
    ctrlTab.panel.state == "showing" || ctrlTab.panel.state == "open";

  function assertTabs(aTabs) {
    var tabs = gBrowser.tabs.length;
    if (tabs != aTabs) {
      while (gBrowser.tabs.length > 1)
        gBrowser.removeCurrentTab();
      throw "expected " + aTabs + " open tabs, got " + tabs;
    }
  }

  function selectTabs(tabs) {
    tabs.forEach(function (index) {
      gBrowser.selectedTab = gBrowser.tabs[index];
    });
  }

  function ctrlTabTest(tabsToSelect, tabTimes, expectedIndex) {
    selectTabs(tabsToSelect);

    var indexStart = gBrowser.tabContainer.selectedIndex;
    var tabCount = gBrowser.tabs.length;
    var normalized = tabTimes % tabCount;
    var where = normalized == 1 ? "back to the previously selected tab" :
                normalized + " tabs back in most-recently-selected order";

    for (let i = 0; i < tabTimes; i++) {
      pressCtrlTab();

      if (tabCount > 2)
       is(gBrowser.tabContainer.selectedIndex, indexStart,
         "Selected tab doesn't change while tabbing");
    }

    if (tabCount > 2) {
      ok(isOpen(),
         "With " + tabCount + " tabs open, Ctrl+Tab opens the preview panel");

      releaseCtrl();

      ok(!isOpen(),
         "Releasing Ctrl closes the preview panel");
    } else {
      ok(!isOpen(),
         "With " + tabCount + " tabs open, Ctrl+Tab doesn't open the preview panel");
    }

    is(gBrowser.tabContainer.selectedIndex, expectedIndex,
       "With "+ tabCount +" tabs open and tab " + indexStart
       + " selected, Ctrl+Tab*" + tabTimes + " goes " + where);
  }
}
