summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/test/test-panel.js
diff options
context:
space:
mode:
Diffstat (limited to 'addon-sdk/source/test/test-panel.js')
-rw-r--r--addon-sdk/source/test/test-panel.js1426
1 files changed, 1426 insertions, 0 deletions
diff --git a/addon-sdk/source/test/test-panel.js b/addon-sdk/source/test/test-panel.js
new file mode 100644
index 000000000..13776e9db
--- /dev/null
+++ b/addon-sdk/source/test/test-panel.js
@@ -0,0 +1,1426 @@
+/* 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';
+
+module.metadata = {
+ 'engines': {
+ 'Firefox': '*'
+ }
+};
+
+const { Cc, Ci } = require("chrome");
+const { Loader } = require('sdk/test/loader');
+const { LoaderWithHookedConsole } = require("sdk/test/loader");
+const { setTimeout } = require("sdk/timers");
+const self = require('sdk/self');
+const { open, close, focus, ready } = require('sdk/window/helpers');
+const { isPrivate } = require('sdk/private-browsing');
+const { isWindowPBSupported } = require('sdk/private-browsing/utils');
+const { defer, all } = require('sdk/core/promise');
+const { getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { URL } = require('sdk/url');
+const { wait } = require('./event/helpers');
+const packaging = require('@loader/options');
+const { cleanUI, after, isTravisCI } = require("sdk/test/utils");
+const { platform } = require('sdk/system');
+
+const fixtures = require('./fixtures')
+
+const SVG_URL = fixtures.url('mofo_logo.SVG');
+
+const Isolate = fn => '(' + fn + ')()';
+
+function ignorePassingDOMNodeWarning(type, message) {
+ if (type !== 'warn' || !message.startsWith('Passing a DOM node'))
+ console[type](message);
+}
+
+function makeEmptyPrivateBrowserWindow(options) {
+ options = options || {};
+ return open('chrome://browser/content/browser.xul', {
+ features: {
+ chrome: true,
+ toolbar: true,
+ private: true
+ }
+ });
+}
+
+exports["test Panel"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let panel = Panel({
+ contentURL: "about:buildconfig",
+ contentScript: "self.postMessage(1); self.on('message', () => self.postMessage(2));",
+ onMessage: function (message) {
+ assert.equal(this, panel, "The 'this' object is the panel.");
+ switch(message) {
+ case 1:
+ assert.pass("The panel was loaded.");
+ panel.postMessage('');
+ break;
+ case 2:
+ assert.pass("The panel posted a message and received a response.");
+ panel.destroy();
+ done();
+ break;
+ }
+ }
+ });
+};
+
+exports["test Panel Emit"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let panel = Panel({
+ contentURL: "about:buildconfig",
+ contentScript: "self.port.emit('loaded');" +
+ "self.port.on('addon-to-content', " +
+ " () => self.port.emit('received'));",
+ });
+ panel.port.on("loaded", function () {
+ assert.pass("The panel was loaded and sent a first event.");
+ panel.port.emit("addon-to-content");
+ });
+ panel.port.on("received", function () {
+ assert.pass("The panel posted a message and received a response.");
+ panel.destroy();
+ done();
+ });
+};
+
+exports["test Panel Emit Early"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let panel = Panel({
+ contentURL: "about:buildconfig",
+ contentScript: "self.port.on('addon-to-content', " +
+ " () => self.port.emit('received'));",
+ });
+ panel.port.on("received", function () {
+ assert.pass("The panel posted a message early and received a response.");
+ panel.destroy();
+ done();
+ });
+ panel.port.emit("addon-to-content");
+};
+
+exports["test Show Hide Panel"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let panel = Panel({
+ contentScript: "self.postMessage('')",
+ contentScriptWhen: "end",
+ contentURL: "data:text/html;charset=utf-8,",
+ onMessage: function (message) {
+ panel.show();
+ },
+ onShow: function () {
+ assert.pass("The panel was shown.");
+ assert.equal(this, panel, "The 'this' object is the panel.");
+ assert.equal(this.isShowing, true, "panel.isShowing == true.");
+ panel.hide();
+ },
+ onHide: function () {
+ assert.pass("The panel was hidden.");
+ assert.equal(this, panel, "The 'this' object is the panel.");
+ assert.equal(this.isShowing, false, "panel.isShowing == false.");
+ panel.destroy();
+ done();
+ }
+ });
+};
+
+exports["test Document Reload"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let url2 = "data:text/html;charset=utf-8,page2";
+ let content =
+ "<script>" +
+ "window.addEventListener('message', function({ data }) {"+
+ " if (data == 'move') window.location = '" + url2 + "';" +
+ '}, false);' +
+ "</script>";
+ let messageCount = 0;
+ let panel = Panel({
+ // using URL here is intentional, see bug 859009
+ contentURL: URL("data:text/html;charset=utf-8," + encodeURIComponent(content)),
+ contentScript: "self.postMessage(window.location.href);" +
+ // initiate change to url2
+ "self.port.once('move', () => document.defaultView.postMessage('move', '*'));",
+ onMessage: function (message) {
+ messageCount++;
+ assert.notEqual(message, "about:blank", "about:blank is not a message " + messageCount);
+
+ if (messageCount == 1) {
+ assert.ok(/data:text\/html/.test(message), "First document had a content script; " + message);
+ panel.port.emit('move');
+ assert.pass('move message was sent');
+ return;
+ }
+ else if (messageCount == 2) {
+ assert.equal(message, url2, "Second document too; " + message);
+ panel.destroy();
+ done();
+ }
+ }
+ });
+ assert.pass('Panel was created');
+};
+
+// Test disabled because of bug 910230
+/*
+exports["test Parent Resize Hack"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let browserWindow = getMostRecentBrowserWindow();
+
+ let previousWidth = browserWindow.outerWidth;
+ let previousHeight = browserWindow.outerHeight;
+
+ let content = "<script>" +
+ "function contentResize() {" +
+ " resizeTo(200,200);" +
+ " resizeBy(200,200);" +
+ " window.postMessage('resize-attempt', '*');" +
+ "}" +
+ "</script>" +
+ "Try to resize browser window";
+
+ let panel = Panel({
+ contentURL: "data:text/html;charset=utf-8," + encodeURIComponent(content),
+ contentScriptWhen: "ready",
+ contentScript: Isolate(() => {
+ self.on('message', message => {
+ if (message === 'resize') unsafeWindow.contentResize();
+ });
+
+ window.addEventListener('message', ({ data }) => self.postMessage(data));
+ }),
+ onMessage: function (message) {
+ if (message !== "resize-attempt") return;
+
+ assert.equal(browserWindow, getMostRecentBrowserWindow(),
+ "The browser window is still the same");
+ assert.equal(previousWidth, browserWindow.outerWidth,
+ "Size doesn't change by calling resizeTo/By/...");
+ assert.equal(previousHeight, browserWindow.outerHeight,
+ "Size doesn't change by calling resizeTo/By/...");
+
+ try {
+ panel.destroy();
+ }
+ catch (e) {
+ assert.fail(e);
+ throw e;
+ }
+
+ done();
+ },
+ onShow: () => panel.postMessage('resize')
+ });
+
+ panel.show();
+}
+*/
+
+exports["test Resize Panel"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ // These tests fail on Linux if the browser window in which the panel
+ // is displayed is not active. And depending on what other tests have run
+ // before this one, it might not be (the untitled window in which the test
+ // runner executes is often active). So we make sure the browser window
+ // is focused by focusing it before running the tests. Then, to be the best
+ // possible test citizen, we refocus whatever window was focused before we
+ // started running these tests.
+
+ let activeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher).
+ activeWindow;
+ let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator).
+ getMostRecentWindow("navigator:browser");
+
+
+ function onFocus() {
+ browserWindow.removeEventListener("focus", onFocus, true);
+
+ let panel = Panel({
+ contentScript: "self.postMessage('')",
+ contentScriptWhen: "end",
+ contentURL: "data:text/html;charset=utf-8,",
+ height: 10,
+ width: 10,
+ onMessage: function (message) {
+ // Make sure that attempting to resize a panel while it isn't
+ // visible doesn't cause an error.
+ panel.resize(1, 1);
+
+ panel.show();
+ },
+ onShow: function () {
+ panel.resize(100,100);
+ panel.hide();
+ },
+ onHide: function () {
+ assert.ok((panel.width == 100) && (panel.height == 100),
+ "The panel was resized.");
+ if (activeWindow)
+ activeWindow.focus();
+ done();
+ }
+ });
+ }
+
+ if (browserWindow === activeWindow) {
+ onFocus();
+ }
+ else {
+ browserWindow.addEventListener("focus", onFocus, true);
+ browserWindow.focus();
+ }
+};
+
+exports["test Hide Before Show"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let showCalled = false;
+ let hideCalled = false;
+ let panel1 = Panel({
+ onShow: function () {
+ showCalled = true;
+ },
+ onHide: function () {
+ hideCalled = true;
+ }
+ });
+ panel1.show();
+ panel1.hide();
+
+ let panel2 = Panel({
+ onShow: function () {
+ if (showCalled) {
+ assert.ok(hideCalled, 'should not emit show without also emitting hide');
+ } else {
+ assert.ok(!hideCalled, 'should not emit hide without also emitting show');
+ }
+ panel1.destroy();
+ panel2.destroy();
+ done();
+ },
+ });
+ panel2.show();
+};
+
+exports["test Several Show Hides"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let hideCalled = 0;
+ let panel = Panel({
+ contentURL: "about:buildconfig",
+ onShow: function () {
+ panel.hide();
+ },
+ onHide: function () {
+ hideCalled++;
+ if (hideCalled < 3)
+ panel.show();
+ else {
+ assert.pass("onHide called three times as expected");
+ done();
+ }
+ }
+ });
+ panel.on('error', function(e) {
+ assert.fail('error was emitted:' + e.message + '\n' + e.stack);
+ });
+ panel.show();
+};
+
+exports["test Anchor And Arrow"] = function*(assert, done) {
+ let { loader } = LoaderWithHookedConsole(module, ignorePassingDOMNodeWarning);
+ let { Panel } = loader.require('sdk/panel');
+
+ let count = 0;
+ let url = 'data:text/html;charset=utf-8,' +
+ '<html><head><title>foo</title></head><body>' +
+ '</body></html>';
+
+ let panel = yield new Promise(resolve => {
+ let browserWindow = getMostRecentBrowserWindow();
+ let anchor = browserWindow.document.getElementById("identity-box");
+ let panel = Panel({
+ contentURL: "data:text/html;charset=utf-8,<html><body style='padding: 0; margin: 0; " +
+ "background: gray; text-align: center;'>Anchor: " +
+ anchor.id + "</body></html>",
+ width: 200,
+ height: 100,
+ onShow: () => resolve(panel)
+ });
+ panel.show(null, anchor);
+ });
+ assert.pass("All anchored panel test displayed");
+
+ panel.destroy();
+ assert.pass("panel was destroyed.");
+};
+
+exports["test Panel Focus True"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ const FM = Cc["@mozilla.org/focus-manager;1"].
+ getService(Ci.nsIFocusManager);
+
+ let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator).
+ getMostRecentWindow("navigator:browser");
+
+ // Make sure there is a focused element
+ browserWindow.document.documentElement.focus();
+
+ // Get the current focused element
+ let focusedElement = FM.focusedElement;
+
+ let panel = Panel({
+ contentURL: "about:buildconfig",
+ focus: true,
+ onShow: function () {
+ assert.ok(focusedElement !== FM.focusedElement,
+ "The panel takes the focus away.");
+ done();
+ }
+ });
+ panel.show();
+};
+
+exports["test Panel Focus False"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ const FM = Cc["@mozilla.org/focus-manager;1"].
+ getService(Ci.nsIFocusManager);
+
+ let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator).
+ getMostRecentWindow("navigator:browser");
+
+ // Make sure there is a focused element
+ browserWindow.document.documentElement.focus();
+
+ // Get the current focused element
+ let focusedElement = FM.focusedElement;
+
+ let panel = Panel({
+ contentURL: "about:buildconfig",
+ focus: false,
+ onShow: function () {
+ assert.ok(focusedElement === FM.focusedElement,
+ "The panel does not take the focus away.");
+ done();
+ }
+ });
+ panel.show();
+};
+
+exports["test Panel Focus Not Set"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ const FM = Cc["@mozilla.org/focus-manager;1"].
+ getService(Ci.nsIFocusManager);
+
+ let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator).
+ getMostRecentWindow("navigator:browser");
+
+ // Make sure there is a focused element
+ browserWindow.document.documentElement.focus();
+
+ // Get the current focused element
+ let focusedElement = FM.focusedElement;
+
+ let panel = Panel({
+ contentURL: "about:buildconfig",
+ onShow: function () {
+ assert.ok(focusedElement !== FM.focusedElement,
+ "The panel takes the focus away.");
+ done();
+ }
+ });
+ panel.show();
+};
+
+exports["test Panel Text Color"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let html = "<html><head><style>body {color: yellow}</style></head>" +
+ "<body><p>Foo</p></body></html>";
+ let panel = Panel({
+ contentURL: "data:text/html;charset=utf-8," + encodeURI(html),
+ contentScript: "self.port.emit('color', " +
+ "window.getComputedStyle(document.body.firstChild, null). " +
+ " getPropertyValue('color'));"
+ });
+ panel.port.on("color", function (color) {
+ assert.equal(color, "rgb(255, 255, 0)",
+ "The panel text color style is preserved when a style exists.");
+ panel.destroy();
+ done();
+ });
+};
+
+// Bug 866333
+exports["test watch event name"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let html = "<html><head><style>body {color: yellow}</style></head>" +
+ "<body><p>Foo</p></body></html>";
+
+ let panel = Panel({
+ contentURL: "data:text/html;charset=utf-8," + encodeURI(html),
+ contentScript: "self.port.emit('watch', 'test');"
+ });
+ panel.port.on("watch", function (msg) {
+ assert.equal(msg, "test", 'watch event name works');
+ panel.destroy();
+ done();
+ });
+}
+
+// Bug 696552: Ensure panel.contentURL modification support
+exports["test Change Content URL"] = function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let panel = Panel({
+ contentURL: "about:blank",
+ contentScript: "self.port.emit('ready', document.location.href);"
+ });
+
+ let count = 0;
+ panel.port.on("ready", function (location) {
+ count++;
+ if (count == 1) {
+ assert.equal(location, "about:blank");
+ assert.equal(panel.contentURL, "about:blank");
+ panel.contentURL = "about:buildconfig";
+ }
+ else {
+ assert.equal(location, "about:buildconfig");
+ assert.equal(panel.contentURL, "about:buildconfig");
+ panel.destroy();
+ done();
+ }
+ });
+};
+
+function makeEventOrderTest(options) {
+ let expectedEvents = [];
+
+ return function(assert, done) {
+ const { Panel } = require('sdk/panel');
+
+ let panel = Panel({ contentURL: "about:buildconfig" });
+
+ function expect(event, cb) {
+ expectedEvents.push(event);
+ panel.on(event, function() {
+ assert.equal(event, expectedEvents.shift());
+ if (cb)
+ setTimeout(cb, 1);
+ });
+ return {then: expect};
+ }
+
+ options.test(assert, done, expect, panel);
+ }
+}
+
+exports["test Automatic Destroy"] = function(assert) {
+ let loader = Loader(module);
+ let panel = loader.require("sdk/panel").Panel({
+ contentURL: "about:buildconfig",
+ contentScript:
+ "self.port.on('event', () => self.port.emit('event-back'));"
+ });
+
+ loader.unload();
+
+ assert.throws(() => {
+ panel.port.emit("event");
+ }, /already have been unloaded/, "check automatic destroy");
+};
+
+exports["test Show Then Destroy"] = makeEventOrderTest({
+ test: function(assert, done, expect, panel) {
+ panel.show();
+ expect('show', function() { panel.destroy(); }).
+ then('hide', function() { done(); });
+ }
+});
+
+
+// TODO: Re-enable and fix this intermittent test
+// See Bug 1111695 https://bugzilla.mozilla.org/show_bug.cgi?id=1111695
+/*
+exports["test Show Then Hide Then Destroy"] = makeEventOrderTest({
+ test: function(assert, done, expect, panel) {
+ panel.show();
+ expect('show', function() { panel.hide(); }).
+ then('hide', function() { panel.destroy(); done(); });
+ }
+});
+*/
+
+exports["test Content URL Option"] = function(assert) {
+ const { Panel } = require('sdk/panel');
+
+ const URL_STRING = "about:buildconfig";
+ const HTML_CONTENT = "<html><title>Test</title><p>This is a test.</p></html>";
+ let dataURL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML_CONTENT);
+
+ let panel = Panel({ contentURL: URL_STRING });
+ assert.pass("contentURL accepts a string URL.");
+ assert.equal(panel.contentURL, URL_STRING,
+ "contentURL is the string to which it was set.");
+ panel.destroy();
+
+ panel = Panel({ contentURL: dataURL });
+ assert.pass("contentURL accepts a data: URL.");
+ panel.destroy();
+
+ panel = Panel({});
+ assert.ok(panel.contentURL == null, "contentURL is undefined.");
+ panel.destroy();
+
+ assert.throws(() => Panel({ contentURL: "foo" }),
+ /The `contentURL` option must be a valid URL./,
+ "Panel throws an exception if contentURL is not a URL.");
+};
+
+exports["test SVG Document"] = function(assert) {
+ let panel = require("sdk/panel").Panel({ contentURL: SVG_URL });
+
+ panel.show();
+ panel.hide();
+ panel.destroy();
+
+ assert.pass("contentURL accepts a svg document");
+ assert.equal(panel.contentURL, SVG_URL,
+ "contentURL is the string to which it was set.");
+};
+
+exports["test ContentScriptOptions Option"] = function(assert, done) {
+ let loader = Loader(module);
+ let panel = loader.require("sdk/panel").Panel({
+ contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
+ contentScriptWhen: "end",
+ contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
+ contentURL: "data:text/html;charset=utf-8,",
+ onMessage: function(msg) {
+ assert.equal( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
+ assert.equal( typeof msg[1], 'object', 'object as contentScriptOptions' );
+ assert.equal( msg[1].a, true, 'boolean in contentScriptOptions' );
+ assert.equal( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
+ assert.equal( msg[1].c, 'string', 'string in contentScriptOptions' );
+ done();
+ }
+ });
+};
+
+exports["test console.log in Panel"] = function(assert, done) {
+ let text = 'console.log() in Panel works!';
+ let html = '<script>onload = function log(){\
+ console.log("' + text + '");\
+ }</script>';
+
+ let { loader } = LoaderWithHookedConsole(module, onMessage);
+ let { Panel } = loader.require('sdk/panel');
+
+ let panel = Panel({
+ contentURL: 'data:text/html;charset=utf-8,' + encodeURIComponent(html)
+ });
+
+ panel.show();
+
+ function onMessage(type, message) {
+ assert.equal(type, 'log', 'console.log() works');
+ assert.equal(message, text, 'console.log() works');
+ panel.destroy();
+ done();
+ }
+};
+
+/*if (isWindowPBSupported) {
+ exports.testPanelDoesNotShowInPrivateWindowNoAnchor = function(assert, done) {
+ let { loader } = LoaderWithHookedConsole(module, ignorePassingDOMNodeWarning);
+ let { Panel } = loader.require("sdk/panel");
+ let browserWindow = getMostRecentBrowserWindow();
+
+ assert.equal(isPrivate(browserWindow), false, 'open window is not private');
+
+ let panel = Panel({
+ contentURL: SVG_URL
+ });
+
+ testShowPanel(assert, panel).
+ then(makeEmptyPrivateBrowserWindow).
+ then(focus).
+ then(function(window) {
+ assert.equal(isPrivate(window), true, 'opened window is private');
+ assert.pass('private window was focused');
+ return window;
+ }).
+ then(function(window) {
+ let { promise, resolve } = defer();
+ let showTries = 0;
+ let showCount = 0;
+
+ panel.on('show', function runTests() {
+ showCount++;
+
+ if (showTries == 2) {
+ panel.removeListener('show', runTests);
+ assert.equal(showCount, 1, 'show count is correct - 1');
+ resolve(window);
+ }
+ });
+ showTries++;
+ panel.show();
+ showTries++;
+ panel.show(null, browserWindow.gBrowser);
+
+ return promise;
+ }).
+ then(function(window) {
+ assert.equal(panel.isShowing, true, 'panel is still showing');
+ panel.hide();
+ assert.equal(panel.isShowing, false, 'panel is hidden');
+ return window;
+ }).
+ then(close).
+ then(function() {
+ assert.pass('private window was closed');
+ }).
+ then(testShowPanel.bind(null, assert, panel)).
+ then(done, assert.fail.bind(assert));
+ }
+
+ exports.testPanelDoesNotShowInPrivateWindowWithAnchor = function(assert, done) {
+ let { loader } = LoaderWithHookedConsole(module, ignorePassingDOMNodeWarning);
+ let { Panel } = loader.require("sdk/panel");
+ let browserWindow = getMostRecentBrowserWindow();
+
+ assert.equal(isPrivate(browserWindow), false, 'open window is not private');
+
+ let panel = Panel({
+ contentURL: SVG_URL
+ });
+
+ testShowPanel(assert, panel).
+ then(makeEmptyPrivateBrowserWindow).
+ then(focus).
+ then(function(window) {
+ assert.equal(isPrivate(window), true, 'opened window is private');
+ assert.pass('private window was focused');
+ return window;
+ }).
+ then(function(window) {
+ let { promise, resolve } = defer();
+ let showTries = 0;
+ let showCount = 0;
+
+ panel.on('show', function runTests() {
+ showCount++;
+
+ if (showTries == 2) {
+ panel.removeListener('show', runTests);
+ assert.equal(showCount, 1, 'show count is correct - 1');
+ resolve(window);
+ }
+ });
+ showTries++;
+ panel.show(null, window.gBrowser);
+ showTries++;
+ panel.show(null, browserWindow.gBrowser);
+
+ return promise;
+ }).
+ then(function(window) {
+ assert.equal(panel.isShowing, true, 'panel is still showing');
+ panel.hide();
+ assert.equal(panel.isShowing, false, 'panel is hidden');
+ return window;
+ }).
+ then(close).
+ then(function() {
+ assert.pass('private window was closed');
+ }).
+ then(testShowPanel.bind(null, assert, panel)).
+ then(done, assert.fail.bind(assert));
+ }
+}*/
+
+function testShowPanel(assert, panel) {
+ let { promise, resolve } = defer();
+
+ assert.ok(!panel.isShowing, 'the panel is not showing [1]');
+
+ panel.once('show', function() {
+ assert.ok(panel.isShowing, 'the panel is showing');
+
+ panel.once('hide', function() {
+ assert.ok(!panel.isShowing, 'the panel is not showing [2]');
+
+ resolve(null);
+ });
+
+ panel.hide();
+ })
+ panel.show();
+
+ return promise;
+}
+
+exports['test Style Applied Only Once'] = function (assert, done) {
+ let loader = Loader(module);
+ let panel = loader.require("sdk/panel").Panel({
+ contentURL: "data:text/html;charset=utf-8,",
+ contentScript:
+ 'self.port.on("check",function() { self.port.emit("count", document.getElementsByTagName("style").length); });' +
+ 'self.port.on("ping", function (count) { self.port.emit("pong", count); });'
+ });
+
+ panel.port.on('count', function (styleCount) {
+ assert.equal(styleCount, 1, 'should only have one style');
+ done();
+ });
+
+ panel.port.on('pong', function (counter) {
+ panel[--counter % 2 ? 'hide' : 'show']();
+ panel.port.emit(!counter ? 'check' : 'ping', counter);
+ });
+
+ panel.on('show', init);
+ panel.show();
+
+ function init () {
+ panel.removeListener('show', init);
+ panel.port.emit('ping', 10);
+ }
+};
+
+exports['test Only One Panel Open Concurrently'] = function (assert, done) {
+ const loader = Loader(module);
+ const { Panel } = loader.require('sdk/panel')
+
+ let panelA = Panel({
+ contentURL: 'about:buildconfig'
+ });
+
+ let panelB = Panel({
+ contentURL: 'about:buildconfig',
+ onShow: function () {
+ // When loading two panels simulataneously, only the second
+ // should be shown, never showing the first
+ assert.equal(panelA.isShowing, false, 'First panel is hidden');
+ assert.equal(panelB.isShowing, true, 'Second panel is showing');
+ panelC.show();
+ }
+ });
+
+ let panelC = Panel({
+ contentURL: 'about:buildconfig',
+ onShow: function () {
+ assert.equal(panelA.isShowing, false, 'First panel is hidden');
+ assert.equal(panelB.isShowing, false, 'Second panel is hidden');
+ assert.equal(panelC.isShowing, true, 'Third panel is showing');
+ done();
+ }
+ });
+
+ panelA.show();
+ panelB.show();
+};
+
+exports['test passing DOM node as first argument'] = function (assert, done) {
+ let warned = defer();
+ let shown = defer();
+
+ function onMessage(type, message) {
+ if (type != 'warn') return;
+
+ let warning = 'Passing a DOM node to Panel.show() method is an unsupported ' +
+ 'feature that will be soon replaced. ' +
+ 'See: https://bugzilla.mozilla.org/show_bug.cgi?id=878877';
+
+ assert.equal(type, 'warn',
+ 'the message logged is a warning');
+
+ assert.equal(message, warning,
+ 'the warning content is correct');
+
+ warned.resolve();
+ }
+
+ let { loader } = LoaderWithHookedConsole(module, onMessage);
+ let { Panel } = loader.require('sdk/panel');
+ let { ActionButton } = loader.require('sdk/ui/button/action');
+ let { getNodeView } = loader.require('sdk/view/core');
+ let { document } = getMostRecentBrowserWindow();
+
+ let panel = Panel({
+ onShow: function() {
+ let panelNode = document.getElementById('mainPopupSet').lastChild;
+
+ assert.equal(panelNode.anchorNode, buttonNode,
+ 'the panel is properly anchored to the button');
+
+ shown.resolve();
+ }
+ });
+
+ let button = ActionButton({
+ id: 'panel-button',
+ label: 'panel button',
+ icon: './icon.png'
+ });
+
+ let buttonNode = getNodeView(button);
+
+ all([warned.promise, shown.promise]).
+ then(loader.unload).
+ then(done, assert.fail)
+
+ panel.show(buttonNode);
+};
+
+// This test is checking that `onpupshowing` events emitted by panel's children
+// are not considered.
+// See Bug 886329
+exports['test nested popups'] = function (assert, done) {
+ let loader = Loader(module);
+ let { Panel } = loader.require('sdk/panel');
+ let { getActiveView } = loader.require('sdk/view/core');
+ let url = '<select><option>1<option>2<option>3</select>';
+
+ let getContentWindow = panel => {
+ return getActiveView(panel).querySelector('iframe').contentWindow;
+ }
+
+ let panel = Panel({
+ contentURL: 'data:text/html;charset=utf-8,' + encodeURIComponent(url),
+ onShow: () => {
+ ready(getContentWindow(panel)).then(({ window, document }) => {
+ let select = document.querySelector('select');
+ let event = document.createEvent('UIEvent');
+
+ event.initUIEvent('popupshowing', true, true, window, null);
+ select.dispatchEvent(event);
+
+ assert.equal(
+ select,
+ getContentWindow(panel).document.querySelector('select'),
+ 'select is still loaded in panel'
+ );
+
+ done();
+ });
+ }
+ });
+
+ panel.show();
+};
+
+exports['test emits on url changes'] = function (assert, done) {
+ let loader = Loader(module);
+ let { Panel } = loader.require('sdk/panel');
+ let uriA = 'data:text/html;charset=utf-8,A';
+ let uriB = 'data:text/html;charset=utf-8,B';
+
+ let panel = Panel({
+ contentURL: uriA,
+ contentScript: 'new ' + function() {
+ self.port.on('hi', function() {
+ self.port.emit('bye', document.URL);
+ });
+ }
+ });
+
+ panel.contentURL = uriB;
+ panel.port.emit('hi', 'hi')
+ panel.port.on('bye', function(uri) {
+ assert.equal(uri, uriB, 'message was delivered to new uri');
+ loader.unload();
+ done();
+ });
+};
+
+exports['test panel can be constructed without any arguments'] = function (assert) {
+ const { Panel } = require('sdk/panel');
+
+ let panel = Panel();
+ assert.ok(true, "Creating a panel with no arguments does not throw");
+};
+
+exports['test panel CSS'] = function(assert, done) {
+ const { merge } = require("sdk/util/object");
+
+ let loader = Loader(module, null, null, {
+ modules: {
+ "sdk/self": merge({}, self, {
+ data: merge({}, self.data, fixtures)
+ })
+ }
+ });
+
+ const { Panel } = loader.require('sdk/panel');
+
+ const { getActiveView } = loader.require('sdk/view/core');
+
+ const getContentWindow = panel =>
+ getActiveView(panel).querySelector('iframe').contentWindow;
+
+ let panel = Panel({
+ contentURL: 'data:text/html;charset=utf-8,' +
+ '<div style="background: silver">css test</div>',
+ contentStyle: 'div { height: 100px; }',
+ contentStyleFile: [fixtures.url("include-file.css"), "./border-style.css"],
+ onShow: () => {
+ ready(getContentWindow(panel)).then(({ window, document }) => {
+ let div = document.querySelector('div');
+
+ assert.equal(div.clientHeight, 100,
+ "Panel contentStyle worked");
+
+ assert.equal(div.offsetHeight, 120,
+ "Panel contentStyleFile worked");
+
+ assert.equal(window.getComputedStyle(div).borderTopStyle, "dashed",
+ "Panel contentStyleFile with relative path worked");
+
+ loader.unload();
+ done();
+ }).then(null, assert.fail);
+ }
+ });
+
+ panel.show();
+};
+
+exports['test panel contentScriptFile'] = function(assert, done) {
+ const { merge } = require("sdk/util/object");
+
+ let loader = Loader(module, null, null, {
+ modules: {
+ "sdk/self": merge({}, self, {
+ data: merge({}, self.data, {url: fixtures.url})
+ })
+ }
+ });
+
+ const { Panel } = loader.require('sdk/panel');
+ const { getActiveView } = loader.require('sdk/view/core');
+
+ const getContentWindow = panel =>
+ getActiveView(panel).querySelector('iframe').contentWindow;
+
+ let whenMessage = defer();
+ let whenShown = defer();
+
+ let panel = Panel({
+ contentURL: './test.html',
+ contentScriptFile: "./test-contentScriptFile.js",
+ onMessage: (message) => {
+ assert.equal(message, "msg from contentScriptFile",
+ "Panel contentScriptFile with relative path worked");
+
+ whenMessage.resolve();
+ },
+ onShow: () => {
+ ready(getContentWindow(panel)).then(({ document }) => {
+ assert.equal(document.title, 'foo',
+ "Panel contentURL with relative path worked");
+
+ whenShown.resolve();
+ });
+ }
+ });
+
+ all([whenMessage.promise, whenShown.promise]).
+ then(loader.unload).
+ then(done, assert.fail);
+
+ panel.show();
+};
+
+
+exports['test panel CSS list'] = function(assert, done) {
+ const loader = Loader(module);
+ const { Panel } = loader.require('sdk/panel');
+
+ const { getActiveView } = loader.require('sdk/view/core');
+
+ const getContentWindow = panel =>
+ getActiveView(panel).querySelector('iframe').contentWindow;
+
+ let panel = Panel({
+ contentURL: 'data:text/html;charset=utf-8,' +
+ '<div style="width:320px; max-width: 480px!important">css test</div>',
+ contentStyleFile: [
+ // Highlight evaluation order in this list
+ "data:text/css;charset=utf-8,div { border: 1px solid black; }",
+ "data:text/css;charset=utf-8,div { border: 10px solid black; }",
+ // Highlight evaluation order between contentStylesheet & contentStylesheetFile
+ "data:text/css;charset=utf-8s,div { height: 1000px; }",
+ // Highlight precedence between the author and user style sheet
+ "data:text/css;charset=utf-8,div { width: 200px; max-width: 640px!important}",
+ ],
+ contentStyle: [
+ "div { height: 10px; }",
+ "div { height: 100px; }"
+ ],
+ onShow: () => {
+ ready(getContentWindow(panel)).then(({ window, document }) => {
+ let div = document.querySelector('div');
+ let style = window.getComputedStyle(div);
+
+ assert.equal(div.clientHeight, 100,
+ 'Panel contentStyle list is evaluated after contentStyleFile');
+
+ assert.equal(div.offsetHeight, 120,
+ 'Panel contentStyleFile list works');
+
+ assert.equal(style.width, '320px',
+ 'add-on author/page author stylesheet precedence works');
+
+ assert.equal(style.maxWidth, '480px',
+ 'add-on author/page author stylesheet !important precedence works');
+
+ loader.unload();
+ }).then(done, assert.fail);
+ }
+ });
+
+ panel.show();
+};
+
+exports['test panel contextmenu validation'] = function(assert) {
+ const loader = Loader(module);
+ const { Panel } = loader.require('sdk/panel');
+
+ let panel = Panel({});
+
+ assert.equal(panel.contextMenu, false,
+ 'contextMenu option is `false` by default');
+
+ panel.destroy();
+
+ panel = Panel({
+ contextMenu: false
+ });
+
+ assert.equal(panel.contextMenu, false,
+ 'contextMenu option is `false`');
+
+ panel.contextMenu = true;
+
+ assert.equal(panel.contextMenu, true,
+ 'contextMenu option accepts boolean values');
+
+ panel.destroy();
+
+ panel = Panel({
+ contextMenu: true
+ });
+
+ assert.equal(panel.contextMenu, true,
+ 'contextMenu option is `true`');
+
+ panel.contextMenu = false;
+
+ assert.equal(panel.contextMenu, false,
+ 'contextMenu option accepts boolean values');
+
+ assert.throws(() =>
+ Panel({contextMenu: 1}),
+ /The option "contextMenu" must be one of the following types: boolean, undefined, null/,
+ 'contextMenu only accepts boolean or nil values');
+
+ panel = Panel();
+
+ assert.throws(() =>
+ panel.contextMenu = 1,
+ /The option "contextMenu" must be one of the following types: boolean, undefined, null/,
+ 'contextMenu only accepts boolean or nil values');
+
+ loader.unload();
+}
+
+exports['test panel contextmenu enabled'] = function*(assert) {
+ const loader = Loader(module);
+ const { Panel } = loader.require('sdk/panel');
+ const { getActiveView } = loader.require('sdk/view/core');
+ const { getContentDocument } = loader.require('sdk/panel/utils');
+
+ let contextmenu = getMostRecentBrowserWindow().
+ document.getElementById("contentAreaContextMenu");
+
+ let panel = Panel({contextMenu: true});
+
+ panel.show();
+
+ yield wait(panel, 'show');
+
+ let view = getActiveView(panel);
+ let window = getContentDocument(view).defaultView;
+
+ let { sendMouseEvent } = window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils);
+
+ yield ready(window);
+
+ assert.equal(contextmenu.state, 'closed',
+ 'contextmenu must be closed');
+
+ sendMouseEvent('contextmenu', 20, 20, 2, 1, 0);
+
+ yield wait(contextmenu, 'popupshown');
+
+ assert.equal(contextmenu.state, 'open',
+ 'contextmenu is opened');
+
+ contextmenu.hidePopup();
+
+ loader.unload();
+}
+
+exports['test panel contextmenu disabled'] = function*(assert) {
+ const loader = Loader(module);
+ const { Panel } = loader.require('sdk/panel');
+ const { getActiveView } = loader.require('sdk/view/core');
+ const { getContentDocument } = loader.require('sdk/panel/utils');
+
+ let contextmenu = getMostRecentBrowserWindow().
+ document.getElementById("contentAreaContextMenu");
+ let listener = () => assert.fail('popupshown should never be called');
+
+ let panel = Panel();
+
+ panel.show();
+
+ yield wait(panel, 'show');
+
+ let view = getActiveView(panel);
+ let window = getContentDocument(view).defaultView;
+
+ let { sendMouseEvent } = window.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils);
+
+ yield ready(window);
+
+ assert.equal(contextmenu.state, 'closed',
+ 'contextmenu must be closed');
+
+ sendMouseEvent('contextmenu', 20, 20, 2, 1, 0);
+
+ contextmenu.addEventListener('popupshown', listener);
+
+ yield wait(1000);
+
+ contextmenu.removeEventListener('popupshown', listener);
+
+ assert.equal(contextmenu.state, 'closed',
+ 'contextmenu was never open');
+
+ loader.unload();
+}
+
+exports["test panel addon global object"] = function*(assert) {
+ const { merge } = require("sdk/util/object");
+
+ let loader = Loader(module, null, null, {
+ modules: {
+ "sdk/self": merge({}, self, {
+ data: merge({}, self.data, {url: fixtures.url})
+ })
+ }
+ });
+
+ const { Panel } = loader.require('sdk/panel');
+
+ let panel = Panel({
+ contentURL: "./test-trusted-document.html"
+ });
+
+ panel.show();
+
+ yield wait(panel, "show");
+
+ panel.port.emit('addon-to-document', 'ok');
+
+ yield wait(panel.port, "document-to-addon");
+
+ assert.pass("Received an event from the document");
+
+ loader.unload();
+}
+
+exports["test panel load doesn't show"] = function*(assert) {
+ let loader = Loader(module);
+
+ let panel = loader.require("sdk/panel").Panel({
+ contentScript: "addEventListener('load', function(event) { self.postMessage('load'); });",
+ contentScriptWhen: "start",
+ contentURL: "data:text/html;charset=utf-8,",
+ });
+
+ let shown = defer();
+ let messaged = defer();
+
+ panel.once("show", function() {
+ shown.resolve();
+ });
+
+ panel.once("message", function() {
+ messaged.resolve();
+ });
+
+ panel.show();
+ yield all([shown.promise, messaged.promise]);
+ assert.ok(true, "Saw panel display");
+
+ panel.on("show", function() {
+ assert.fail("Should not have seen another show event")
+ });
+
+ messaged = defer();
+ panel.once("message", function() {
+ assert.ok(true, "Saw panel reload");
+ messaged.resolve();
+ });
+
+ panel.contentURL = "data:text/html;charset=utf-8,<html/>";
+
+ yield messaged.promise;
+ loader.unload();
+}
+
+exports["test Panel without contentURL and contentScriptWhen=start should show"] = function*(assert) {
+ let loader = Loader(module);
+
+ let panel = loader.require("sdk/panel").Panel({
+ contentScriptWhen: "start",
+ // No contentURL, the bug only shows up when contentURL is not explicitly set.
+ });
+
+ yield new Promise(resolve => {
+ panel.once("show", resolve);
+ panel.show();
+ });
+
+ assert.pass("Received show event");
+
+ loader.unload();
+}
+
+exports["test Panel links"] = function*(assert) {
+ const loader = Loader(module);
+
+ const { Panel } = loader.require('sdk/panel');
+ const { getActiveView } = loader.require('sdk/view/core');
+ const tabs = loader.require('sdk/tabs');
+
+ const synthesizeClick = (panel, options) => {
+ let { contentWindow } = getActiveView(panel).querySelector('iframe');
+ let event = new contentWindow.MouseEvent('click', options);
+
+ contentWindow.document.querySelector('a').dispatchEvent(event);
+ }
+
+ const linkURL = 'data:text/html;charset=utf-8,' +
+ encodeURIComponent('<html><a href="#">foo</a></html>');
+
+ const contentURL = 'data:text/html;charset=utf-8,' +
+ encodeURIComponent(`<html><a href="${linkURL}">page</a></html>`);
+
+ let panel = Panel({
+ contentURL,
+ contentScript: Isolate(() => self.postMessage(document.URL))
+ });
+
+ panel.show();
+
+ let url = yield wait(panel, 'message');
+
+ assert.equal(url, contentURL,
+ 'content URL loaded');
+
+ synthesizeClick(panel, { bubbles: true });
+
+ url = yield wait(panel, 'message');
+
+ assert.equal(url, linkURL,
+ 'link URL loaded in the panel after click');
+
+ synthesizeClick(panel, {
+ bubbles: true,
+ [platform === 'darwin' ? 'metaKey' : 'ctrlKey']: true
+ });
+
+ let tab = yield wait(tabs, 'ready');
+
+ assert.equal(tab.url, linkURL + '#',
+ 'link URL loaded in a new tab after click + accel');
+
+ loader.unload();
+}
+
+exports["test Panel script allow property"] = function*(assert) {
+ const loader = Loader(module);
+ const { Panel } = loader.require('sdk/panel');
+ const { getActiveView } = loader.require('sdk/view/core');
+
+ const contentURL = 'data:text/html;charset=utf-8,' +
+ encodeURIComponent(`<body onclick='postMessage("got script click", "*")'>
+ <script>
+ document.body.appendChild(document.createElement('unwanted'))
+ </script>
+ </body>`);
+ let panel = Panel({
+ contentURL,
+ allow: {script: false},
+ });
+
+ panel.show();
+
+ yield wait(panel, 'show');
+
+ let { contentWindow } = getActiveView(panel).querySelector('iframe');
+
+ assert.equal(contentWindow.document.body.lastElementChild.localName, "script",
+ "Script should not have executed");
+
+ panel.allow.script = true;
+
+ let p = wait(contentWindow, "message");
+ let event = new contentWindow.MouseEvent('click', {});
+ contentWindow.document.body.dispatchEvent(event);
+
+ let msg = yield p;
+ assert.equal(msg.data, "got script click", "Should have seen script click");
+
+ loader.unload();
+};
+
+after(exports, function*(name, assert) {
+ yield cleanUI();
+ assert.pass("ui was cleaned.");
+});
+
+if (isTravisCI) {
+ module.exports = {
+ "test skip on jpm": (assert) => assert.pass("skipping this file with jpm")
+ };
+}
+
+require("sdk/test").run(exports);