summaryrefslogtreecommitdiffstats
path: root/browser/components/customizableui/test/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/customizableui/test/head.js')
-rw-r--r--browser/components/customizableui/test/head.js499
1 files changed, 499 insertions, 0 deletions
diff --git a/browser/components/customizableui/test/head.js b/browser/components/customizableui/test/head.js
new file mode 100644
index 000000000..7b8d84e20
--- /dev/null
+++ b/browser/components/customizableui/test/head.js
@@ -0,0 +1,499 @@
+/* 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";
+
+// Avoid leaks by using tmp for imports...
+var tmp = {};
+Cu.import("resource://gre/modules/Promise.jsm", tmp);
+Cu.import("resource:///modules/CustomizableUI.jsm", tmp);
+Cu.import("resource://gre/modules/AppConstants.jsm", tmp);
+var {Promise, CustomizableUI, AppConstants} = tmp;
+
+var EventUtils = {};
+Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+
+Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
+registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck"));
+
+// Remove temporary e10s related new window options in customize ui,
+// they break a lot of tests.
+CustomizableUI.destroyWidget("e10s-button");
+CustomizableUI.removeWidgetFromArea("e10s-button");
+
+var {synthesizeDragStart, synthesizeDrop} = EventUtils;
+
+const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const kTabEventFailureTimeoutInMs = 20000;
+
+function createDummyXULButton(id, label, win = window) {
+ let btn = document.createElementNS(kNSXUL, "toolbarbutton");
+ btn.id = id;
+ btn.setAttribute("label", label || id);
+ btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
+ win.gNavToolbox.palette.appendChild(btn);
+ return btn;
+}
+
+var gAddedToolbars = new Set();
+
+function createToolbarWithPlacements(id, placements = []) {
+ gAddedToolbars.add(id);
+ let tb = document.createElementNS(kNSXUL, "toolbar");
+ tb.id = id;
+ tb.setAttribute("customizable", "true");
+ CustomizableUI.registerArea(id, {
+ type: CustomizableUI.TYPE_TOOLBAR,
+ defaultPlacements: placements
+ });
+ gNavToolbox.appendChild(tb);
+ return tb;
+}
+
+function createOverflowableToolbarWithPlacements(id, placements) {
+ gAddedToolbars.add(id);
+
+ let tb = document.createElementNS(kNSXUL, "toolbar");
+ tb.id = id;
+ tb.setAttribute("customizationtarget", id + "-target");
+
+ let customizationtarget = document.createElementNS(kNSXUL, "hbox");
+ customizationtarget.id = id + "-target";
+ customizationtarget.setAttribute("flex", "1");
+ tb.appendChild(customizationtarget);
+
+ let overflowPanel = document.createElementNS(kNSXUL, "panel");
+ overflowPanel.id = id + "-overflow";
+ document.getElementById("mainPopupSet").appendChild(overflowPanel);
+
+ let overflowList = document.createElementNS(kNSXUL, "vbox");
+ overflowList.id = id + "-overflow-list";
+ overflowPanel.appendChild(overflowList);
+
+ let chevron = document.createElementNS(kNSXUL, "toolbarbutton");
+ chevron.id = id + "-chevron";
+ tb.appendChild(chevron);
+
+ CustomizableUI.registerArea(id, {
+ type: CustomizableUI.TYPE_TOOLBAR,
+ defaultPlacements: placements,
+ overflowable: true,
+ });
+
+ tb.setAttribute("customizable", "true");
+ tb.setAttribute("overflowable", "true");
+ tb.setAttribute("overflowpanel", overflowPanel.id);
+ tb.setAttribute("overflowtarget", overflowList.id);
+ tb.setAttribute("overflowbutton", chevron.id);
+
+ gNavToolbox.appendChild(tb);
+ return tb;
+}
+
+function removeCustomToolbars() {
+ CustomizableUI.reset();
+ for (let toolbarId of gAddedToolbars) {
+ CustomizableUI.unregisterArea(toolbarId, true);
+ let tb = document.getElementById(toolbarId);
+ if (tb.hasAttribute("overflowpanel")) {
+ let panel = document.getElementById(tb.getAttribute("overflowpanel"));
+ if (panel)
+ panel.remove();
+ }
+ tb.remove();
+ }
+ gAddedToolbars.clear();
+}
+
+function getToolboxCustomToolbarId(toolbarName) {
+ return "__customToolbar_" + toolbarName.replace(" ", "_");
+}
+
+function resetCustomization() {
+ return CustomizableUI.reset();
+}
+
+function isInDevEdition() {
+ return AppConstants.MOZ_DEV_EDITION;
+}
+
+function removeDeveloperButtonIfDevEdition(areaPanelPlacements) {
+ if (isInDevEdition()) {
+ areaPanelPlacements.splice(areaPanelPlacements.indexOf("developer-button"), 1);
+ }
+}
+
+function assertAreaPlacements(areaId, expectedPlacements) {
+ let actualPlacements = getAreaWidgetIds(areaId);
+ placementArraysEqual(areaId, actualPlacements, expectedPlacements);
+}
+
+function placementArraysEqual(areaId, actualPlacements, expectedPlacements) {
+ is(actualPlacements.length, expectedPlacements.length,
+ "Area " + areaId + " should have " + expectedPlacements.length + " items.");
+ let minItems = Math.min(expectedPlacements.length, actualPlacements.length);
+ for (let i = 0; i < minItems; i++) {
+ if (typeof expectedPlacements[i] == "string") {
+ is(actualPlacements[i], expectedPlacements[i],
+ "Item " + i + " in " + areaId + " should match expectations.");
+ } else if (expectedPlacements[i] instanceof RegExp) {
+ ok(expectedPlacements[i].test(actualPlacements[i]),
+ "Item " + i + " (" + actualPlacements[i] + ") in " +
+ areaId + " should match " + expectedPlacements[i]);
+ } else {
+ ok(false, "Unknown type of expected placement passed to " +
+ " assertAreaPlacements. Is your test broken?");
+ }
+ }
+}
+
+function todoAssertAreaPlacements(areaId, expectedPlacements) {
+ let actualPlacements = getAreaWidgetIds(areaId);
+ let isPassing = actualPlacements.length == expectedPlacements.length;
+ let minItems = Math.min(expectedPlacements.length, actualPlacements.length);
+ for (let i = 0; i < minItems; i++) {
+ if (typeof expectedPlacements[i] == "string") {
+ isPassing = isPassing && actualPlacements[i] == expectedPlacements[i];
+ } else if (expectedPlacements[i] instanceof RegExp) {
+ isPassing = isPassing && expectedPlacements[i].test(actualPlacements[i]);
+ } else {
+ ok(false, "Unknown type of expected placement passed to " +
+ " assertAreaPlacements. Is your test broken?");
+ }
+ }
+ todo(isPassing, "The area placements for " + areaId +
+ " should equal the expected placements.");
+}
+
+function getAreaWidgetIds(areaId) {
+ return CustomizableUI.getWidgetIdsInArea(areaId);
+}
+
+function simulateItemDrag(aToDrag, aTarget) {
+ synthesizeDrop(aToDrag.parentNode, aTarget);
+}
+
+function endCustomizing(aWindow=window) {
+ if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
+ return true;
+ }
+ Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
+ let deferredEndCustomizing = Promise.defer();
+ function onCustomizationEnds() {
+ Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", false);
+ aWindow.gNavToolbox.removeEventListener("aftercustomization", onCustomizationEnds);
+ deferredEndCustomizing.resolve();
+ }
+ aWindow.gNavToolbox.addEventListener("aftercustomization", onCustomizationEnds);
+ aWindow.gCustomizeMode.exit();
+
+ return deferredEndCustomizing.promise;
+}
+
+function startCustomizing(aWindow=window) {
+ if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
+ return null;
+ }
+ Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
+ let deferred = Promise.defer();
+ function onCustomizing() {
+ aWindow.gNavToolbox.removeEventListener("customizationready", onCustomizing);
+ Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", false);
+ deferred.resolve();
+ }
+ aWindow.gNavToolbox.addEventListener("customizationready", onCustomizing);
+ aWindow.gCustomizeMode.enter();
+ return deferred.promise;
+}
+
+function promiseObserverNotified(aTopic) {
+ let deferred = Promise.defer();
+ Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
+ Services.obs.removeObserver(onNotification, aTopic);
+ deferred.resolve({subject: aSubject, data: aData});
+ }, aTopic, false);
+ return deferred.promise;
+}
+
+function openAndLoadWindow(aOptions, aWaitForDelayedStartup=false) {
+ let deferred = Promise.defer();
+ let win = OpenBrowserWindow(aOptions);
+ if (aWaitForDelayedStartup) {
+ Services.obs.addObserver(function onDS(aSubject, aTopic, aData) {
+ if (aSubject != win) {
+ return;
+ }
+ Services.obs.removeObserver(onDS, "browser-delayed-startup-finished");
+ deferred.resolve(win);
+ }, "browser-delayed-startup-finished", false);
+
+ } else {
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad);
+ deferred.resolve(win);
+ });
+ }
+ return deferred.promise;
+}
+
+function promiseWindowClosed(win) {
+ let deferred = Promise.defer();
+ win.addEventListener("unload", function onunload() {
+ win.removeEventListener("unload", onunload);
+ deferred.resolve();
+ });
+ win.close();
+ return deferred.promise;
+}
+
+function promisePanelShown(win) {
+ let panelEl = win.PanelUI.panel;
+ return promisePanelElementShown(win, panelEl);
+}
+
+function promiseOverflowShown(win) {
+ let panelEl = win.document.getElementById("widget-overflow");
+ return promisePanelElementShown(win, panelEl);
+}
+
+function promisePanelElementShown(win, aPanel) {
+ let deferred = Promise.defer();
+ let timeoutId = win.setTimeout(() => {
+ deferred.reject("Panel did not show within 20 seconds.");
+ }, 20000);
+ function onPanelOpen(e) {
+ aPanel.removeEventListener("popupshown", onPanelOpen);
+ win.clearTimeout(timeoutId);
+ deferred.resolve();
+ }
+ aPanel.addEventListener("popupshown", onPanelOpen);
+ return deferred.promise;
+}
+
+function promisePanelHidden(win) {
+ let panelEl = win.PanelUI.panel;
+ return promisePanelElementHidden(win, panelEl);
+}
+
+function promiseOverflowHidden(win) {
+ let panelEl = document.getElementById("widget-overflow");
+ return promisePanelElementHidden(win, panelEl);
+}
+
+function promisePanelElementHidden(win, aPanel) {
+ let deferred = Promise.defer();
+ let timeoutId = win.setTimeout(() => {
+ deferred.reject("Panel did not hide within 20 seconds.");
+ }, 20000);
+ function onPanelClose(e) {
+ aPanel.removeEventListener("popuphidden", onPanelClose);
+ win.clearTimeout(timeoutId);
+ deferred.resolve();
+ }
+ aPanel.addEventListener("popuphidden", onPanelClose);
+ return deferred.promise;
+}
+
+function isPanelUIOpen() {
+ return PanelUI.panel.state == "open" || PanelUI.panel.state == "showing";
+}
+
+function subviewShown(aSubview) {
+ let deferred = Promise.defer();
+ let win = aSubview.ownerGlobal;
+ let timeoutId = win.setTimeout(() => {
+ deferred.reject("Subview (" + aSubview.id + ") did not show within 20 seconds.");
+ }, 20000);
+ function onViewShowing(e) {
+ aSubview.removeEventListener("ViewShowing", onViewShowing);
+ win.clearTimeout(timeoutId);
+ deferred.resolve();
+ }
+ aSubview.addEventListener("ViewShowing", onViewShowing);
+ return deferred.promise;
+}
+
+function subviewHidden(aSubview) {
+ let deferred = Promise.defer();
+ let win = aSubview.ownerGlobal;
+ let timeoutId = win.setTimeout(() => {
+ deferred.reject("Subview (" + aSubview.id + ") did not hide within 20 seconds.");
+ }, 20000);
+ function onViewHiding(e) {
+ aSubview.removeEventListener("ViewHiding", onViewHiding);
+ win.clearTimeout(timeoutId);
+ deferred.resolve();
+ }
+ aSubview.addEventListener("ViewHiding", onViewHiding);
+ return deferred.promise;
+}
+
+function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
+ function tryNow() {
+ tries++;
+ if (aConditionFn()) {
+ deferred.resolve();
+ } else if (tries < aMaxTries) {
+ tryAgain();
+ } else {
+ deferred.reject("Condition timed out: " + aConditionFn.toSource());
+ }
+ }
+ function tryAgain() {
+ setTimeout(tryNow, aCheckInterval);
+ }
+ let deferred = Promise.defer();
+ let tries = 0;
+ tryAgain();
+ return deferred.promise;
+}
+
+function waitFor(aTimeout=100) {
+ let deferred = Promise.defer();
+ setTimeout(() => deferred.resolve(), aTimeout);
+ return deferred.promise;
+}
+
+/**
+ * Starts a load in an existing tab and waits for it to finish (via some event).
+ *
+ * @param aTab The tab to load into.
+ * @param aUrl The url to load.
+ * @param aEventType The load event type to wait for. Defaults to "load".
+ * @return {Promise} resolved when the event is handled.
+ */
+function promiseTabLoadEvent(aTab, aURL) {
+ let browser = aTab.linkedBrowser;
+
+ BrowserTestUtils.loadURI(browser, aURL);
+ return BrowserTestUtils.browserLoaded(browser);
+}
+
+/**
+ * Navigate back or forward in tab history and wait for it to finish.
+ *
+ * @param aDirection Number to indicate to move backward or forward in history.
+ * @param aConditionFn Function that returns the result of an evaluated condition
+ * that needs to be `true` to resolve the promise.
+ * @return {Promise} resolved when navigation has finished.
+ */
+function promiseTabHistoryNavigation(aDirection = -1, aConditionFn) {
+ let deferred = Promise.defer();
+
+ let timeoutId = setTimeout(() => {
+ gBrowser.removeEventListener("pageshow", listener, true);
+ deferred.reject("Pageshow did not happen within " + kTabEventFailureTimeoutInMs + "ms");
+ }, kTabEventFailureTimeoutInMs);
+
+ function listener(event) {
+ gBrowser.removeEventListener("pageshow", listener, true);
+ clearTimeout(timeoutId);
+
+ if (aConditionFn) {
+ waitForCondition(aConditionFn).then(() => deferred.resolve(),
+ aReason => deferred.reject(aReason));
+ } else {
+ deferred.resolve();
+ }
+ }
+ gBrowser.addEventListener("pageshow", listener, true);
+
+ content.history.go(aDirection);
+
+ return deferred.promise;
+}
+
+/**
+ * Wait for an attribute on a node to change
+ *
+ * @param aNode Node on which the mutation is expected
+ * @param aAttribute The attribute we're interested in
+ * @param aFilterFn A function to check if the new value is what we want.
+ * @return {Promise} resolved when the requisite mutation shows up.
+ */
+function promiseAttributeMutation(aNode, aAttribute, aFilterFn) {
+ return new Promise((resolve, reject) => {
+ info("waiting for mutation of attribute '" + aAttribute + "'.");
+ let obs = new MutationObserver((mutations) => {
+ for (let mut of mutations) {
+ let attr = mut.attributeName;
+ let newValue = mut.target.getAttribute(attr);
+ if (aFilterFn(newValue)) {
+ ok(true, "mutation occurred: attribute '" + attr + "' changed to '" + newValue + "' from '" + mut.oldValue + "'.");
+ obs.disconnect();
+ resolve();
+ } else {
+ info("Ignoring mutation that produced value " + newValue + " because of filter.");
+ }
+ }
+ });
+ obs.observe(aNode, {attributeFilter: [aAttribute]});
+ });
+}
+
+function popupShown(aPopup) {
+ return promisePopupEvent(aPopup, "shown");
+}
+
+function popupHidden(aPopup) {
+ return promisePopupEvent(aPopup, "hidden");
+}
+
+/**
+ * Returns a Promise that resolves when aPopup fires an event of type
+ * aEventType. Times out and rejects after 20 seconds.
+ *
+ * @param aPopup the popup to monitor for events.
+ * @param aEventSuffix the _suffix_ for the popup event type to watch for.
+ *
+ * Example usage:
+ * let popupShownPromise = promisePopupEvent(somePopup, "shown");
+ * // ... something that opens a popup
+ * yield popupShownPromise;
+ *
+ * let popupHiddenPromise = promisePopupEvent(somePopup, "hidden");
+ * // ... something that hides a popup
+ * yield popupHiddenPromise;
+ */
+function promisePopupEvent(aPopup, aEventSuffix) {
+ let deferred = Promise.defer();
+ let eventType = "popup" + aEventSuffix;
+
+ function onPopupEvent(e) {
+ aPopup.removeEventListener(eventType, onPopupEvent);
+ deferred.resolve();
+ }
+
+ aPopup.addEventListener(eventType, onPopupEvent);
+ return deferred.promise;
+}
+
+// This is a simpler version of the context menu check that
+// exists in contextmenu_common.js.
+function checkContextMenu(aContextMenu, aExpectedEntries, aWindow=window) {
+ let childNodes = [...aContextMenu.childNodes];
+ // Ignore hidden nodes:
+ childNodes = childNodes.filter((n) => !n.hidden);
+
+ for (let i = 0; i < childNodes.length; i++) {
+ let menuitem = childNodes[i];
+ try {
+ if (aExpectedEntries[i][0] == "---") {
+ is(menuitem.localName, "menuseparator", "menuseparator expected");
+ continue;
+ }
+
+ let selector = aExpectedEntries[i][0];
+ ok(menuitem.matches(selector), "menuitem should match " + selector + " selector");
+ let commandValue = menuitem.getAttribute("command");
+ let relatedCommand = commandValue ? aWindow.document.getElementById(commandValue) : null;
+ let menuItemDisabled = relatedCommand ?
+ relatedCommand.getAttribute("disabled") == "true" :
+ menuitem.getAttribute("disabled") == "true";
+ is(menuItemDisabled, !aExpectedEntries[i][1], "disabled state for " + selector);
+ } catch (e) {
+ ok(false, "Exception when checking context menu: " + e);
+ }
+ }
+}