forked from mirrors/gecko-dev
Differential Revision: https://phabricator.services.mozilla.com/D3552 --HG-- extra : moz-landing-system : lando
337 lines
12 KiB
JavaScript
337 lines
12 KiB
JavaScript
/* eslint-env mozilla/frame-script */
|
|
|
|
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
|
|
"resource://gre/modules/PlacesUtils.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
|
|
"resource://testing-common/PlacesTestUtils.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "Preferences",
|
|
"resource://gre/modules/Preferences.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "HttpServer",
|
|
"resource://testing-common/httpd.js");
|
|
ChromeUtils.defineModuleGetter(this, "SearchTestUtils",
|
|
"resource://testing-common/SearchTestUtils.jsm");
|
|
|
|
SearchTestUtils.init(Assert, registerCleanupFunction);
|
|
|
|
/**
|
|
* Waits for the next top-level document load in the current browser. The URI
|
|
* of the document is compared against aExpectedURL. The load is then stopped
|
|
* before it actually starts.
|
|
*
|
|
* @param aExpectedURL
|
|
* The URL of the document that is expected to load.
|
|
* @param aStopFromProgressListener
|
|
* Whether to cancel the load directly from the progress listener. Defaults to true.
|
|
* If you're using this method to avoid hitting the network, you want the default (true).
|
|
* However, the browser UI will behave differently for loads stopped directly from
|
|
* the progress listener (effectively in the middle of a call to loadURI) and so there
|
|
* are cases where you may want to avoid stopping the load directly from within the
|
|
* progress listener callback.
|
|
* @return promise
|
|
*/
|
|
function waitForDocLoadAndStopIt(aExpectedURL, aBrowser = gBrowser.selectedBrowser, aStopFromProgressListener = true) {
|
|
function content_script(contentStopFromProgressListener) {
|
|
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
let wp = docShell.QueryInterface(Ci.nsIWebProgress);
|
|
|
|
function stopContent(now, uri) {
|
|
if (now) {
|
|
/* Hammer time. */
|
|
content.stop();
|
|
|
|
/* Let the parent know we're done. */
|
|
sendAsyncMessage("Test:WaitForDocLoadAndStopIt", { uri });
|
|
} else {
|
|
setTimeout(stopContent.bind(null, true, uri), 0);
|
|
}
|
|
}
|
|
|
|
let progressListener = {
|
|
onStateChange(webProgress, req, flags, status) {
|
|
dump("waitForDocLoadAndStopIt: onStateChange " + flags.toString(16) + ": " + req.name + "\n");
|
|
|
|
if (webProgress.isTopLevel &&
|
|
flags & Ci.nsIWebProgressListener.STATE_START) {
|
|
wp.removeProgressListener(progressListener);
|
|
|
|
let chan = req.QueryInterface(Ci.nsIChannel);
|
|
dump(`waitForDocLoadAndStopIt: Document start: ${chan.URI.spec}\n`);
|
|
|
|
stopContent(contentStopFromProgressListener, chan.originalURI.spec);
|
|
}
|
|
},
|
|
QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"])
|
|
};
|
|
wp.addProgressListener(progressListener, wp.NOTIFY_STATE_WINDOW);
|
|
|
|
/**
|
|
* As |this| is undefined and we can't extend |docShell|, adding an unload
|
|
* event handler is the easiest way to ensure the weakly referenced
|
|
* progress listener is kept alive as long as necessary.
|
|
*/
|
|
addEventListener("unload", function() {
|
|
try {
|
|
wp.removeProgressListener(progressListener);
|
|
} catch (e) { /* Will most likely fail. */ }
|
|
});
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
function complete({ data }) {
|
|
is(data.uri, aExpectedURL, "waitForDocLoadAndStopIt: The expected URL was loaded");
|
|
mm.removeMessageListener("Test:WaitForDocLoadAndStopIt", complete);
|
|
resolve();
|
|
}
|
|
|
|
let mm = aBrowser.messageManager;
|
|
mm.loadFrameScript("data:,(" + content_script.toString() + ")(" + aStopFromProgressListener + ");", true);
|
|
mm.addMessageListener("Test:WaitForDocLoadAndStopIt", complete);
|
|
info("waitForDocLoadAndStopIt: Waiting for URL: " + aExpectedURL);
|
|
});
|
|
}
|
|
|
|
function is_element_visible(element, msg) {
|
|
isnot(element, null, "Element should not be null, when checking visibility");
|
|
ok(BrowserTestUtils.is_visible(element), msg || "Element should be visible");
|
|
}
|
|
|
|
function is_element_hidden(element, msg) {
|
|
isnot(element, null, "Element should not be null, when checking visibility");
|
|
ok(BrowserTestUtils.is_hidden(element), msg || "Element should be hidden");
|
|
}
|
|
|
|
function runHttpServer(scheme, host, port = -1) {
|
|
let httpserver = new HttpServer();
|
|
try {
|
|
httpserver.start(port);
|
|
port = httpserver.identity.primaryPort;
|
|
httpserver.identity.setPrimary(scheme, host, port);
|
|
} catch (ex) {
|
|
info("We can't launch our http server successfully.");
|
|
}
|
|
is(httpserver.identity.has(scheme, host, port), true, `${scheme}://${host}:${port} is listening.`);
|
|
return httpserver;
|
|
}
|
|
|
|
function promisePopupEvent(popup, eventSuffix) {
|
|
let endState = {shown: "open", hidden: "closed"}[eventSuffix];
|
|
|
|
if (popup.state == endState)
|
|
return Promise.resolve();
|
|
|
|
let eventType = "popup" + eventSuffix;
|
|
return new Promise(resolve => {
|
|
popup.addEventListener(eventType, function(event) {
|
|
resolve();
|
|
}, {once: true});
|
|
|
|
});
|
|
}
|
|
|
|
function promisePopupShown(popup) {
|
|
return promisePopupEvent(popup, "shown");
|
|
}
|
|
|
|
function promisePopupHidden(popup) {
|
|
return promisePopupEvent(popup, "hidden");
|
|
}
|
|
|
|
function promiseSearchComplete(win = window, dontAnimate = false) {
|
|
return promisePopupShown(win.gURLBar.popup).then(() => {
|
|
function searchIsComplete() {
|
|
let isComplete = win.gURLBar.controller.searchStatus >=
|
|
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH;
|
|
if (isComplete) {
|
|
info(`Restore popup dontAnimate value to ${dontAnimate}`);
|
|
win.gURLBar.popup.setAttribute("dontanimate", dontAnimate);
|
|
}
|
|
return isComplete;
|
|
}
|
|
|
|
// Wait until there are at least two matches.
|
|
return BrowserTestUtils.waitForCondition(searchIsComplete, "waiting urlbar search to complete");
|
|
});
|
|
}
|
|
|
|
function promiseAutocompleteResultPopup(inputText,
|
|
win = window,
|
|
fireInputEvent = false) {
|
|
let dontAnimate = !!win.gURLBar.popup.getAttribute("dontanimate");
|
|
waitForFocus(() => {
|
|
info(`Disable popup animation. Change dontAnimate value from ${dontAnimate} to true.`);
|
|
win.gURLBar.popup.setAttribute("dontanimate", "true");
|
|
win.gURLBar.focus();
|
|
win.gURLBar.value = inputText;
|
|
if (fireInputEvent) {
|
|
// This is necessary to get the urlbar to set gBrowser.userTypedValue.
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("input", true, true);
|
|
win.gURLBar.dispatchEvent(event);
|
|
}
|
|
win.gURLBar.controller.startSearch(inputText);
|
|
}, win);
|
|
|
|
return promiseSearchComplete(win, dontAnimate);
|
|
}
|
|
|
|
function promisePageActionPanelOpen() {
|
|
let dwu = window.windowUtils;
|
|
return BrowserTestUtils.waitForCondition(() => {
|
|
// Wait for the main page action button to become visible. It's hidden for
|
|
// some URIs, so depending on when this is called, it may not yet be quite
|
|
// visible. It's up to the caller to make sure it will be visible.
|
|
info("Waiting for main page action button to have non-0 size");
|
|
let bounds = dwu.getBoundsWithoutFlushing(BrowserPageActions.mainButtonNode);
|
|
return bounds.width > 0 && bounds.height > 0;
|
|
}).then(() => {
|
|
// Wait for the panel to become open, by clicking the button if necessary.
|
|
info("Waiting for main page action panel to be open");
|
|
if (BrowserPageActions.panelNode.state == "open") {
|
|
return Promise.resolve();
|
|
}
|
|
let shownPromise = promisePageActionPanelShown();
|
|
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
|
|
return shownPromise;
|
|
}).then(() => {
|
|
// Wait for items in the panel to become visible.
|
|
return promisePageActionViewChildrenVisible(BrowserPageActions.mainViewNode);
|
|
});
|
|
}
|
|
|
|
async function waitForActivatedActionPanel() {
|
|
if (!BrowserPageActions.activatedActionPanelNode) {
|
|
info("Waiting for activated-action panel to be added to mainPopupSet");
|
|
await new Promise(resolve => {
|
|
let observer = new MutationObserver(mutations => {
|
|
if (BrowserPageActions.activatedActionPanelNode) {
|
|
observer.disconnect();
|
|
resolve();
|
|
}
|
|
});
|
|
let popupSet = document.getElementById("mainPopupSet");
|
|
observer.observe(popupSet, { childList: true });
|
|
});
|
|
info("Activated-action panel added to mainPopupSet");
|
|
}
|
|
if (!BrowserPageActions.activatedActionPanelNode.state == "open") {
|
|
info("Waiting for activated-action panel popupshown");
|
|
await promisePanelShown(BrowserPageActions.activatedActionPanelNode);
|
|
info("Got activated-action panel popupshown");
|
|
}
|
|
let panelView =
|
|
BrowserPageActions.activatedActionPanelNode.querySelector("panelview");
|
|
if (panelView) {
|
|
await BrowserTestUtils.waitForEvent(
|
|
BrowserPageActions.activatedActionPanelNode,
|
|
"ViewShown"
|
|
);
|
|
await promisePageActionViewChildrenVisible(panelView);
|
|
}
|
|
return panelView;
|
|
}
|
|
|
|
function promisePageActionPanelShown() {
|
|
return promisePanelShown(BrowserPageActions.panelNode);
|
|
}
|
|
|
|
function promisePageActionPanelHidden() {
|
|
return promisePanelHidden(BrowserPageActions.panelNode);
|
|
}
|
|
|
|
function promisePanelShown(panelIDOrNode) {
|
|
return promisePanelEvent(panelIDOrNode, "popupshown");
|
|
}
|
|
|
|
function promisePanelHidden(panelIDOrNode) {
|
|
return promisePanelEvent(panelIDOrNode, "popuphidden");
|
|
}
|
|
|
|
function promisePanelEvent(panelIDOrNode, eventType) {
|
|
return new Promise(resolve => {
|
|
let panel = panelIDOrNode;
|
|
if (typeof panel == "string") {
|
|
panel = document.getElementById(panelIDOrNode);
|
|
if (!panel) {
|
|
throw new Error(`Panel with ID "${panelIDOrNode}" does not exist.`);
|
|
}
|
|
}
|
|
if ((eventType == "popupshown" && panel.state == "open") ||
|
|
(eventType == "popuphidden" && panel.state == "closed")) {
|
|
executeSoon(resolve);
|
|
return;
|
|
}
|
|
panel.addEventListener(eventType, () => {
|
|
executeSoon(resolve);
|
|
}, { once: true });
|
|
});
|
|
}
|
|
|
|
function promisePageActionViewShown() {
|
|
info("promisePageActionViewShown waiting for ViewShown");
|
|
return BrowserTestUtils.waitForEvent(BrowserPageActions.panelNode, "ViewShown").then(async event => {
|
|
let panelViewNode = event.originalTarget;
|
|
await promisePageActionViewChildrenVisible(panelViewNode);
|
|
return panelViewNode;
|
|
});
|
|
}
|
|
|
|
function promisePageActionViewChildrenVisible(panelViewNode) {
|
|
return promiseNodeVisible(panelViewNode.firstChild.firstChild);
|
|
}
|
|
|
|
function promiseNodeVisible(node) {
|
|
info(`promiseNodeVisible waiting, node.id=${node.id} node.localeName=${node.localName}\n`);
|
|
let dwu = window.windowUtils;
|
|
return BrowserTestUtils.waitForCondition(() => {
|
|
let bounds = dwu.getBoundsWithoutFlushing(node);
|
|
if (bounds.width > 0 && bounds.height > 0) {
|
|
info(`promiseNodeVisible OK, node.id=${node.id} node.localeName=${node.localName}\n`);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
function promiseSpeculativeConnection(httpserver) {
|
|
return BrowserTestUtils.waitForCondition(() => {
|
|
if (httpserver) {
|
|
return httpserver.connectionNumber == 1;
|
|
}
|
|
return false;
|
|
}, "Waiting for connection setup");
|
|
}
|
|
|
|
async function waitForAutocompleteResultAt(index) {
|
|
let searchString = gURLBar.controller.searchString;
|
|
await BrowserTestUtils.waitForCondition(
|
|
() => gURLBar.popup.richlistbox.children.length > index &&
|
|
gURLBar.popup.richlistbox.children[index].getAttribute("ac-text") == searchString.trim(),
|
|
`Waiting for the autocomplete result for "${searchString}" at [${index}] to appear`);
|
|
// Ensure the addition is complete, for proper mouse events on the entries.
|
|
await new Promise(resolve => window.requestIdleCallback(resolve, {timeout: 1000}));
|
|
return gURLBar.popup.richlistbox.children[index];
|
|
}
|
|
|
|
function promiseSuggestionsPresent(msg = "") {
|
|
return TestUtils.waitForCondition(suggestionsPresent,
|
|
msg || "Waiting for suggestions");
|
|
}
|
|
|
|
function suggestionsPresent() {
|
|
let controller = gURLBar.popup.input.controller;
|
|
let matchCount = controller.matchCount;
|
|
for (let i = 0; i < matchCount; i++) {
|
|
let url = controller.getValueAt(i);
|
|
let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
|
|
if (mozActionMatch) {
|
|
let [, type, paramStr] = mozActionMatch;
|
|
let params = JSON.parse(paramStr);
|
|
if (type == "searchengine" && "searchSuggestion" in params) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|