fune/browser/base/content/test/urlbar/head.js
Marco Bonardo 223e6dec30 Bug 1402286 - chunk notifyResults calls so that we don't run all the autocomplete js for each match. r=adw
MozReview-Commit-ID: GuYew5B30WQ

--HG--
extra : rebase_source : 236f6fe83728ddbb9d73c4f7606aca2187e267b5
2017-10-31 11:13:47 +01:00

326 lines
12 KiB
JavaScript

/* eslint-env mozilla/frame-script */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
"resource://testing-common/httpd.js");
/**
* 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) {
let { interfaces: Ci, utils: Cu } = Components;
Components.utils.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: XPCOMUtils.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_hidden(element) {
var style = element.ownerGlobal.getComputedStyle(element);
if (style.display == "none")
return true;
if (style.visibility != "visible")
return true;
if (style.display == "-moz-popup")
return ["hiding", "closed"].indexOf(element.state) != -1;
// Hiding a parent element will hide all its children
if (element.parentNode != element.ownerDocument)
return is_hidden(element.parentNode);
return false;
}
function is_visible(element) {
var style = element.ownerGlobal.getComputedStyle(element);
if (style.display == "none")
return false;
if (style.visibility != "visible")
return false;
if (style.display == "-moz-popup" && element.state != "open")
return false;
// Hiding a parent element will hide all its children
if (element.parentNode != element.ownerDocument)
return is_visible(element.parentNode);
return true;
}
function is_element_visible(element, msg) {
isnot(element, null, "Element should not be null, when checking visibility");
ok(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(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 promiseNewSearchEngine(basename) {
return new Promise((resolve, reject) => {
info("Waiting for engine to be added: " + basename);
let url = getRootDirectory(gTestPath) + basename;
Services.search.addEngine(url, null, "", false, {
onSuccess(engine) {
info("Search engine added: " + basename);
registerCleanupFunction(() => Services.search.removeEngine(engine));
resolve(engine);
},
onError(errCode) {
Assert.ok(false, "addEngine failed with error code " + errCode);
reject();
},
});
});
}
function promisePageActionPanelOpen() {
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
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);
});
}
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 = typeof(panelIDOrNode) != "string" ? panelIDOrNode :
document.getElementById(panelIDOrNode);
if (!panel ||
(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) {
info("promisePageActionViewChildrenVisible waiting for a child node to be visible");
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return BrowserTestUtils.waitForCondition(() => {
let bodyNode = panelViewNode.firstChild;
for (let childNode of bodyNode.childNodes) {
let bounds = dwu.getBoundsWithoutFlushing(childNode);
if (bounds.width > 0 && bounds.height > 0) {
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,
`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];
}