fune/browser/base/content/test/urlbar/browser_urlbarAddonIframe.js
Jared Wein ecab54a7c9 Bug 1325464 - Enable object-shorthand rule and run 'mach eslint --fix' with the rule enabled. r=MattN
MozReview-Commit-ID: 7E7LPorrEje

--HG--
extra : rebase_source : 0572a35415a766a3f31d266760ecd07f0dcc3f72
2016-12-29 18:34:54 -05:00

220 lines
7.9 KiB
JavaScript

"use strict";
// The purpose of this test is to test the urlbar popup's add-on iframe. It has
// a few parts:
//
// (1) This file, a normal browser mochitest.
// (2) html/js files that are loaded in the urlbar popup's add-on iframe:
// urlbarAddonIframe.{html,js}
// (3) A content script that mediates between the first two parts:
// urlbarAddonIframeContentScript.js
//
// The main test file (this file) sends messages to the content script, which
// forwards them as events to the iframe. These messages tell the iframe js to
// do various things like call functions on the urlbar API and expect events.
// In response, the iframe js dispatches ack events to the content script, which
// forwards them as messages to the main test file.
//
// The content script may not be necessary right now since the iframe is not
// remote. But this structure ensures that if the iframe is made remote in the
// future, then the test won't have to change very much, and ideally not at all.
//
// Actually there's one other part:
//
// (4) The Panel.jsm that's bundled with add-ons that use the iframe.
//
// Panel.jsm defines the API that's made available to add-on scripts running in
// the iframe. This API is orthogonal to the add-on iframe itself. You could
// load any html/js in the iframe, technically. But the purpose of the iframe
// is to support this Panel.jsm API, so that's what this test tests.
const PANEL_JSM_BASENAME = "Panel.jsm";
const IFRAME_BASENAME = "urlbarAddonIframe.html";
const CONTENT_SCRIPT_BASENAME = "urlbarAddonIframeContentScript.js";
// The iframe's message manager.
let gMsgMan;
add_task(function* () {
let rootDirURL = getRootDirectory(gTestPath);
let jsmURL = rootDirURL + PANEL_JSM_BASENAME;
let iframeURL = rootDirURL + IFRAME_BASENAME;
let contentScriptURL = rootDirURL + CONTENT_SCRIPT_BASENAME;
let { Panel } = Cu.import(jsmURL, {});
let panel = new Panel(gURLBar.popup, iframeURL);
registerCleanupFunction(() => {
panel.destroy();
Assert.ok(gURLBar.popup._addonIframe === null, "iframe should be gone");
});
let iframe = gURLBar.popup._addonIframe;
Assert.ok(!!iframe, "iframe should not be null");
gMsgMan =
iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
gMsgMan.loadFrameScript(contentScriptURL, false);
yield promiseIframeLoad();
// urlbar.getValue
let value = "this value set by the test";
gURLBar.value = value;
let readValue = yield promiseUrlbarFunctionCall("getValue");
Assert.equal(readValue, value, "value");
// urlbar.setValue
value = "this value set by the iframe";
yield promiseUrlbarFunctionCall("setValue", value);
Assert.equal(gURLBar.value, value, "setValue");
// urlbar.getMaxResults
let maxResults = gURLBar.popup.maxResults;
Assert.equal(typeof(maxResults), "number", "Sanity check");
let readMaxResults = yield promiseUrlbarFunctionCall("getMaxResults");
Assert.equal(readMaxResults, maxResults, "getMaxResults");
// urlbar.setMaxResults
let newMaxResults = maxResults + 10;
yield promiseUrlbarFunctionCall("setMaxResults", newMaxResults);
Assert.equal(gURLBar.popup.maxResults, newMaxResults, "setMaxResults");
gURLBar.popup.maxResults = maxResults;
// urlbar.enter
value = "http://mochi.test:8888/";
yield promiseUrlbarFunctionCall("setValue", value);
Assert.equal(gURLBar.value, value, "setValue");
yield promiseUrlbarFunctionCall("enter");
let browser = gBrowser.selectedBrowser;
yield BrowserTestUtils.browserLoaded(browser);
Assert.equal(browser.currentURI.spec, value,
"enter should have loaded the URL");
// input, reset, and result events. There should always be at least one
// result, the heuristic result.
value = "test";
let promiseValues = yield Promise.all([
promiseEvent("input")[1],
promiseEvent("reset")[1],
promiseEvent("result")[1],
promiseAutocompleteResultPopup(value, window, true),
]);
// Check the heuristic result.
let result = promiseValues[2];
let engineName = Services.search.currentEngine.name;
Assert.equal(result.url,
`moz-action:searchengine,{"engineName":"${engineName}","input":"test","searchQuery":"test"}`,
"result.url");
Assert.ok("action" in result, "result.action");
Assert.equal(result.action.type, "searchengine", "result.action.type");
Assert.ok("params" in result.action, "result.action.params");
Assert.equal(result.action.params.engineName, engineName,
"result.action.params.engineName");
Assert.equal(typeof(result.image), "string", "result.image");
Assert.equal(result.title, engineName, "result.title");
Assert.equal(result.type, "action searchengine heuristic", "result.type");
Assert.equal(result.text, value, "result.text");
// keydown event. promiseEvent sends an async message to the iframe, but
// synthesizeKey is sync, so we need to wait until the content JS receives
// the message and adds its event listener before synthesizing the key.
let keydownPromises = promiseEvent("keydown");
yield keydownPromises[0];
EventUtils.synthesizeKey("KEY_ArrowDown", {
type: "keydown",
code: "ArrowDown",
});
yield keydownPromises[1];
// urlbar.getPanelHeight
let height = iframe.getBoundingClientRect().height;
let readHeight = yield promiseUrlbarFunctionCall("getPanelHeight");
Assert.equal(readHeight, height, "getPanelHeight");
// urlbar.setPanelHeight
let newHeight = height + 100;
yield promiseUrlbarFunctionCall("setPanelHeight", newHeight);
yield new Promise(resolve => {
// The height change is animated, so give it time to complete. Again, wait
// a sec to be safe.
setTimeout(resolve, 1000);
});
Assert.equal(iframe.getBoundingClientRect().height, newHeight,
"setPanelHeight");
});
function promiseIframeLoad() {
let msgName = "TestIframeLoadAck";
return new Promise(resolve => {
info("Waiting for iframe load ack");
gMsgMan.addMessageListener(msgName, function onMsg(msg) {
info("Received iframe load ack");
gMsgMan.removeMessageListener(msgName, onMsg);
resolve();
});
});
}
/**
* Returns a single promise that's resolved when the content JS has called the
* function.
*/
function promiseUrlbarFunctionCall(...args) {
return promiseMessage("function", args)[0];
}
/**
* Returns two promises in an array. The first is resolved when the content JS
* has added its event listener. The second is resolved when the content JS
* has received the event.
*/
function promiseEvent(type) {
return promiseMessage("event", type, 2);
}
let gNextMessageID = 1;
/**
* Returns an array of promises, one per ack. Each is resolved when the content
* JS acks the message. numExpectedAcks is the number of acks you expect.
*/
function promiseMessage(type, data, numExpectedAcks = 1) {
let testMsgName = "TestMessage";
let ackMsgName = "TestMessageAck";
let msgID = gNextMessageID++;
gMsgMan.sendAsyncMessage(testMsgName, {
type,
messageID: msgID,
data,
});
let ackPromises = [];
for (let i = 0; i < numExpectedAcks; i++) {
let ackIndex = i;
ackPromises.push(new Promise(resolve => {
info("Waiting for message ack: " + JSON.stringify({
type,
msgID,
ackIndex,
}));
gMsgMan.addMessageListener(ackMsgName, function onMsg(msg) {
// Messages have IDs so that an ack can be correctly paired with the
// initial message it's replying to. It's not an error if the ack's ID
// isn't equal to msgID here. That will happen when multiple messages
// have been sent in a single turn of the event loop so that they're all
// waiting on acks. Same goes for ackIndex.
if (msg.data.messageID != msgID || msg.data.ackIndex != ackIndex) {
return;
}
info("Received message ack: " + JSON.stringify({
type,
msgID: msg.data.messageID,
ackIndex,
}));
gMsgMan.removeMessageListener(ackMsgName, onMsg);
resolve(msg.data.data);
});
}));
}
return ackPromises;
}