diff options
Diffstat (limited to 'toolkit/content/tests/browser/head.js')
-rw-r--r-- | toolkit/content/tests/browser/head.js | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/toolkit/content/tests/browser/head.js b/toolkit/content/tests/browser/head.js new file mode 100644 index 0000000000..9684d495e8 --- /dev/null +++ b/toolkit/content/tests/browser/head.js @@ -0,0 +1,405 @@ +"use strict"; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +/** + * Set the findbar value to the given text, start a search for that text, and + * return a promise that resolves when the find has completed. + * + * @param gBrowser tabbrowser to search in the current tab. + * @param searchText text to search for. + * @param highlightOn true if highlight mode should be enabled before searching. + * @returns Promise resolves when find is complete. + */ +async function promiseFindFinished(gBrowser, searchText, highlightOn = false) { + let findbar = await gBrowser.getFindBar(); + findbar.startFind(findbar.FIND_NORMAL); + let highlightElement = findbar.getElement("highlight"); + if (highlightElement.checked != highlightOn) { + highlightElement.click(); + } + return new Promise(resolve => { + executeSoon(() => { + findbar._findField.value = searchText; + + let resultListener; + // When highlighting is on the finder sends a second "FOUND" message after + // the search wraps. This causes timing problems with e10s. waitMore + // forces foundOrTimeout wait for the second "FOUND" message before + // resolving the promise. + let waitMore = highlightOn; + let findTimeout = setTimeout(() => foundOrTimedout(null), 5000); + let foundOrTimedout = function(aData) { + if (aData !== null && waitMore) { + waitMore = false; + return; + } + if (aData === null) { + info("Result listener not called, timeout reached."); + } + clearTimeout(findTimeout); + findbar.browser?.finder.removeResultListener(resultListener); + resolve(); + }; + + resultListener = { + onFindResult: foundOrTimedout, + onCurrentSelection() {}, + onMatchesCountResult() {}, + onHighlightFinished() {}, + }; + findbar.browser.finder.addResultListener(resultListener); + findbar._find(); + }); + }); +} + +/** + * A wrapper for the findbar's method "close", which is not synchronous + * because of animation. + */ +function closeFindbarAndWait(findbar) { + return new Promise(resolve => { + if (findbar.hidden) { + resolve(); + return; + } + findbar.addEventListener("transitionend", function cont(aEvent) { + if (aEvent.propertyName != "visibility") { + return; + } + findbar.removeEventListener("transitionend", cont); + resolve(); + }); + let close = findbar.getElement("find-closebutton"); + close.doCommand(); + }); +} + +function pushPrefs(...aPrefs) { + return new Promise(resolve => { + SpecialPowers.pushPrefEnv({ set: aPrefs }, resolve); + }); +} + +/** + * Used to check whether the audio unblocking icon is in the tab. + */ +async function waitForTabBlockEvent(tab, expectBlocked) { + if (tab.activeMediaBlocked == expectBlocked) { + ok(true, "The tab should " + (expectBlocked ? "" : "not ") + "be blocked"); + } else { + info("Block state doens't match, wait for attributes changes."); + await BrowserTestUtils.waitForEvent( + tab, + "TabAttrModified", + false, + event => { + if (event.detail.changed.includes("activemedia-blocked")) { + is( + tab.activeMediaBlocked, + expectBlocked, + "The tab should " + (expectBlocked ? "" : "not ") + "be blocked" + ); + return true; + } + return false; + } + ); + } +} + +/** + * Used to check whether the tab has soundplaying attribute. + */ +async function waitForTabPlayingEvent(tab, expectPlaying) { + if (tab.soundPlaying == expectPlaying) { + ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing"); + } else { + info("Playing state doesn't match, wait for attributes changes."); + await BrowserTestUtils.waitForEvent( + tab, + "TabAttrModified", + false, + event => { + if (event.detail.changed.includes("soundplaying")) { + is( + tab.soundPlaying, + expectPlaying, + "The tab should " + (expectPlaying ? "" : "not ") + "be playing" + ); + return true; + } + return false; + } + ); + } +} + +function disable_non_test_mouse(disable) { + let utils = window.windowUtils; + utils.disableNonTestMouseEvents(disable); +} + +function hover_icon(icon, tooltip) { + disable_non_test_mouse(true); + + let popupShownPromise = BrowserTestUtils.waitForEvent(tooltip, "popupshown"); + EventUtils.synthesizeMouse(icon, 1, 1, { type: "mouseover" }); + EventUtils.synthesizeMouse(icon, 2, 2, { type: "mousemove" }); + EventUtils.synthesizeMouse(icon, 3, 3, { type: "mousemove" }); + EventUtils.synthesizeMouse(icon, 4, 4, { type: "mousemove" }); + return popupShownPromise; +} + +function leave_icon(icon) { + EventUtils.synthesizeMouse(icon, 0, 0, { type: "mouseout" }); + EventUtils.synthesizeMouseAtCenter(document.documentElement, { + type: "mousemove", + }); + EventUtils.synthesizeMouseAtCenter(document.documentElement, { + type: "mousemove", + }); + EventUtils.synthesizeMouseAtCenter(document.documentElement, { + type: "mousemove", + }); + + disable_non_test_mouse(false); +} + +/** + * Helper class for testing datetime input picker widget + */ +class DateTimeTestHelper { + constructor() { + this.panel = gBrowser._getAndMaybeCreateDateTimePickerPanel(); + this.panel.setAttribute("animate", false); + this.tab = null; + this.frame = null; + } + + /** + * Opens a new tab with the URL of the test page, and make sure the picker is + * ready for testing. + * + * @param {String} pageUrl + * @param {bool} inFrame true if input is in the first child frame + * @param {String} openMethod "click" or "showPicker" + */ + async openPicker(pageUrl, inFrame, openMethod = "click") { + this.tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl); + let bc = gBrowser.selectedBrowser; + if (inFrame) { + await SpecialPowers.spawn(bc, [], async function() { + const iframe = content.document.querySelector("iframe"); + // Ensure the iframe's position is correct before doing any + // other operations + iframe.getBoundingClientRect(); + }); + bc = bc.browsingContext.children[0]; + } + await SpecialPowers.spawn(bc, [], async function() { + // Ensure that screen coordinates are ok. + await SpecialPowers.contentTransformsReceived(content); + }); + + if (openMethod === "click") { + await SpecialPowers.spawn(bc, [], () => { + const input = content.document.querySelector("input"); + const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot; + shadowRoot.getElementById("calendar-button").click(); + }); + } else if (openMethod === "showPicker") { + await SpecialPowers.spawn(bc, [], function() { + content.document.notifyUserGestureActivation(); + content.document.querySelector("input").showPicker(); + }); + } + this.frame = this.panel.querySelector("#dateTimePopupFrame"); + await this.waitForPickerReady(); + } + + promisePickerClosed() { + return new Promise(resolve => { + this.panel.addEventListener("popuphidden", resolve, { once: true }); + }); + } + + promiseChange(selector = "input") { + return SpecialPowers.spawn( + this.tab.linkedBrowser, + [selector], + async selector => { + let input = content.document.querySelector(selector); + await ContentTaskUtils.waitForEvent(input, "change", false, e => { + ok( + content.window.windowUtils.isHandlingUserInput, + "isHandlingUserInput should be true" + ); + return true; + }); + } + ); + } + + async waitForPickerReady() { + let readyPromise; + let loadPromise = new Promise(resolve => { + let listener = () => { + if ( + this.frame.browsingContext.currentURI.spec != + "chrome://global/content/datepicker.xhtml" + ) { + return; + } + + this.frame.removeEventListener("load", listener, { capture: true }); + // Add the PickerReady event listener directly inside the load event + // listener to avoid missing the event. + readyPromise = BrowserTestUtils.waitForEvent( + this.frame.contentDocument, + "PickerReady" + ); + resolve(); + }; + + this.frame.addEventListener("load", listener, { capture: true }); + }); + + await loadPromise; + // Wait for picker elements to be ready + await readyPromise; + } + + /** + * Find an element on the picker. + * + * @param {String} selector + * @return {DOMElement} + */ + getElement(selector) { + return this.frame.contentDocument.querySelector(selector); + } + + /** + * Find the children of an element on the picker. + * + * @param {String} selector + * @return {Array<DOMElement>} + */ + getChildren(selector) { + return Array.from(this.getElement(selector).children); + } + + /** + * Click on an element + * + * @param {DOMElement} element + */ + click(element) { + EventUtils.synthesizeMouseAtCenter(element, {}, this.frame.contentWindow); + } + + /** + * Close the panel and the tab + */ + async tearDown() { + if (this.panel.state != "closed") { + let pickerClosePromise = this.promisePickerClosed(); + this.panel.hidePopup(); + await pickerClosePromise; + } + BrowserTestUtils.removeTab(this.tab); + this.tab = null; + } + + /** + * Clean up after tests. Remove the frame to prevent leak. + */ + cleanup() { + this.frame.remove(); + this.frame = null; + this.panel.removeAttribute("animate"); + this.panel = null; + } +} + +/** + * Used to listen events if you just need it once + */ +function once(target, name) { + var p = new Promise(function(resolve, reject) { + target.addEventListener( + name, + function() { + resolve(); + }, + { once: true } + ); + }); + return p; +} + +/** + * check if current wakelock is equal to expected state, if not, then wait until + * the wakelock changes its state to expected state. + * @param needLock + * the wakolock should be locked or not + * @param isForegroundLock + * when the lock is on, the wakelock should be in the foreground or not + */ +async function waitForExpectedWakeLockState( + topic, + { needLock, isForegroundLock } +) { + const powerManagerService = Cc["@mozilla.org/power/powermanagerservice;1"]; + const powerManager = powerManagerService.getService( + Ci.nsIPowerManagerService + ); + const wakelockState = powerManager.getWakeLockState(topic); + let expectedLockState = "unlocked"; + if (needLock) { + expectedLockState = isForegroundLock + ? "locked-foreground" + : "locked-background"; + } + if (wakelockState != expectedLockState) { + info(`wait until wakelock becomes ${expectedLockState}`); + await wakeLockObserved( + powerManager, + topic, + state => state == expectedLockState + ); + } + is( + powerManager.getWakeLockState(topic), + expectedLockState, + `the wakelock state for '${topic}' is equal to '${expectedLockState}'` + ); +} + +function wakeLockObserved(powerManager, observeTopic, checkFn) { + return new Promise(resolve => { + function wakeLockListener() {} + wakeLockListener.prototype = { + QueryInterface: ChromeUtils.generateQI(["nsIDOMMozWakeLockListener"]), + callback(topic, state) { + if (topic == observeTopic && checkFn(state)) { + powerManager.removeWakeLockListener(wakeLockListener.prototype); + resolve(); + } + }, + }; + powerManager.addWakeLockListener(wakeLockListener.prototype); + }); +} + +function getTestWebBasedURL(fileName, { crossOrigin = false } = {}) { + const origin = crossOrigin ? "http://example.org" : "http://example.com"; + return ( + getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) + + fileName + ); +} |