/* 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 = ""; 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 = "" + "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) { 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 () { assert.ok(!showCalled, 'should not emit show'); assert.ok(!hideCalled, 'should not emit hide'); 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,' + 'foo' + ''; 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,Anchor: " + anchor.id + "", 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 = "" + "

Foo

"; 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 = "" + "

Foo

"; 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 = "Test

This is a test.

"; 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 = ''; 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 = ''; 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,' + '
css test
', 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,' + '
css test
', 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,"; 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('foo'); const contentURL = 'data:text/html;charset=utf-8,' + encodeURIComponent(`page`); 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(` `); 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);