12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247 |
- /* 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 widgets = require("sdk/widget");
- const { Cc, Ci, Cu } = require("chrome");
- const { Loader } = require('sdk/test/loader');
- const url = require("sdk/url");
- const timer = require("sdk/timers");
- const self = require("sdk/self");
- const windowUtils = require("sdk/deprecated/window-utils");
- const { getMostRecentBrowserWindow } = require('sdk/window/utils');
- const { close, open, focus } = require("sdk/window/helpers");
- const tabs = require("sdk/tabs/utils");
- const { merge } = require("sdk/util/object");
- const unload = require("sdk/system/unload");
- const fixtures = require("./fixtures");
- let jetpackID = "testID";
- try {
- jetpackID = require("sdk/self").id;
- } catch(e) {}
- const australis = !!require("sdk/window/utils").getMostRecentBrowserWindow().CustomizableUI;
- function openNewWindowTab(url, options) {
- return open('chrome://browser/content/browser.xul', {
- features: {
- chrome: true,
- toolbar: true
- }
- }).then(focus).then(function(window) {
- if (options.onLoad) {
- options.onLoad({ target: { defaultView: window } })
- }
- return newTab;
- });
- }
- exports.testConstructor = function(assert, done) {
- let browserWindow = windowUtils.activeBrowserWindow;
- let doc = browserWindow.document;
- let AddonsMgrListener;
- if (australis) {
- AddonsMgrListener = {
- onInstalling: () => {},
- onInstalled: () => {},
- onUninstalling: () => {},
- onUninstalled: () => {}
- };
- }
- else {
- AddonsMgrListener = browserWindow.AddonsMgrListener;
- }
- function container() australis ? doc.getElementById("nav-bar") : doc.getElementById("addon-bar");
- function getWidgets() container() ? container().querySelectorAll('[id^="widget\:"]') : [];
- function widgetCount() getWidgets().length;
- let widgetStartCount = widgetCount();
- function widgetNode(index) getWidgets()[index];
- // Test basic construct/destroy
- AddonsMgrListener.onInstalling();
- let w = widgets.Widget({ id: "basic-construct-destroy", label: "foo", content: "bar" });
- AddonsMgrListener.onInstalled();
- assert.equal(widgetCount(), widgetStartCount + 1, "panel has correct number of child elements after widget construction");
- // test widget height
- assert.equal(widgetNode(0).firstChild.boxObject.height, 16, "widget has correct default height");
- AddonsMgrListener.onUninstalling();
- w.destroy();
- AddonsMgrListener.onUninstalled();
- w.destroy();
- assert.pass("Multiple destroys do not cause an error");
- assert.equal(widgetCount(), widgetStartCount, "panel has correct number of child elements after destroy");
- // Test automatic widget destroy on unload
- let loader = Loader(module);
- let widgetsFromLoader = loader.require("sdk/widget");
- let widgetStartCount = widgetCount();
- let w = widgetsFromLoader.Widget({ id: "destroy-on-unload", label: "foo", content: "bar" });
- assert.equal(widgetCount(), widgetStartCount + 1, "widget has been correctly added");
- loader.unload();
- assert.equal(widgetCount(), widgetStartCount, "widget has been destroyed on module unload");
- // Test nothing
- assert.throws(
- function() widgets.Widget({}),
- /^The widget must have a non-empty label property\.$/,
- "throws on no properties");
- // Test no label
- assert.throws(
- function() widgets.Widget({content: "foo"}),
- /^The widget must have a non-empty label property\.$/,
- "throws on no label");
- // Test empty label
- assert.throws(
- function() widgets.Widget({label: "", content: "foo"}),
- /^The widget must have a non-empty label property\.$/,
- "throws on empty label");
- // Test no content or image
- assert.throws(
- function() widgets.Widget({id: "no-content-throws", label: "foo"}),
- /^No content or contentURL property found\. Widgets must have one or the other\.$/,
- "throws on no content");
- // Test empty content, no image
- assert.throws(
- function() widgets.Widget({id:"empty-content-throws", label: "foo", content: ""}),
- /^No content or contentURL property found\. Widgets must have one or the other\.$/,
- "throws on empty content");
- // Test empty image, no content
- assert.throws(
- function() widgets.Widget({id:"empty-image-throws", label: "foo", image: ""}),
- /^No content or contentURL property found\. Widgets must have one or the other\.$/,
- "throws on empty content");
- // Test empty content, empty image
- assert.throws(
- function() widgets.Widget({id:"empty-image-and-content-throws", label: "foo", content: "", image: ""}),
- /^No content or contentURL property found. Widgets must have one or the other\.$/,
- "throws on empty content");
- // Test duplicated ID
- let duplicateID = widgets.Widget({id: "foo", label: "foo", content: "bar"});
- assert.throws(
- function() widgets.Widget({id: "foo", label: "bar", content: "bar"}),
- /^This widget ID is already used: foo$/,
- "throws on duplicated id");
- duplicateID.destroy();
- // Test Bug 652527
- assert.throws(
- function() widgets.Widget({id: "", label: "bar", content: "bar"}),
- /^You have to specify a unique value for the id property of your widget in order for the application to remember its position\./,
- "throws on falsey id");
- // Test duplicate label, different ID
- let w1 = widgets.Widget({id: "id1", label: "foo", content: "bar"});
- let w2 = widgets.Widget({id: "id2", label: "foo", content: "bar"});
- w1.destroy();
- w2.destroy();
- // Test position restore on create/destroy/create
- // Create 3 ordered widgets
- let w1 = widgets.Widget({id: "position-first", label:"first", content: "bar"});
- let w2 = widgets.Widget({id: "position-second", label:"second", content: "bar"});
- let w3 = widgets.Widget({id: "position-third", label:"third", content: "bar"});
- // Remove the middle widget
- assert.equal(widgetNode(1).getAttribute("label"), "second", "second widget is the second widget inserted");
- w2.destroy();
- assert.equal(widgetNode(1).getAttribute("label"), "third", "second widget is removed, so second widget is now the third one");
- w2 = widgets.Widget({id: "position-second", label:"second", content: "bar"});
- assert.equal(widgetNode(1).getAttribute("label"), "second", "second widget is created again, at the same location");
- // Cleanup this testcase
- AddonsMgrListener.onUninstalling();
- w1.destroy();
- w2.destroy();
- w3.destroy();
- AddonsMgrListener.onUninstalled();
- // Test concurrent widget module instances on addon-bar hiding
- if (!australis) {
- let loader = Loader(module);
- let anotherWidgetsInstance = loader.require("sdk/widget");
- assert.ok(container().collapsed, "UI is hidden when no widgets");
- AddonsMgrListener.onInstalling();
- let w1 = widgets.Widget({id: "ui-unhide", label: "foo", content: "bar"});
- // Ideally we would let AddonsMgrListener display the addon bar
- // But, for now, addon bar is immediatly displayed by sdk code
- // https://bugzilla.mozilla.org/show_bug.cgi?id=627484
- assert.ok(!container().collapsed, "UI is already visible when we just added the widget");
- AddonsMgrListener.onInstalled();
- assert.ok(!container().collapsed, "UI become visible when we notify AddonsMgrListener about end of addon installation");
- let w2 = anotherWidgetsInstance.Widget({id: "ui-stay-open", label: "bar", content: "foo"});
- assert.ok(!container().collapsed, "UI still visible when we add a second widget");
- AddonsMgrListener.onUninstalling();
- w1.destroy();
- AddonsMgrListener.onUninstalled();
- assert.ok(!container().collapsed, "UI still visible when we remove one of two widgets");
- AddonsMgrListener.onUninstalling();
- w2.destroy();
- assert.ok(!container().collapsed, "UI is still visible when we have removed all widget but still not called onUninstalled");
- AddonsMgrListener.onUninstalled();
- assert.ok(container().collapsed, "UI is hidden when we have removed all widget and called onUninstalled");
- }
- // Helper for testing a single widget.
- // Confirms proper addition and content setup.
- function testSingleWidget(widgetOptions) {
- // We have to display which test is being run, because here we do not
- // use the regular test framework but rather a custom one that iterates
- // the `tests` array.
- console.info("executing: " + widgetOptions.id);
- let startCount = widgetCount();
- let widget = widgets.Widget(widgetOptions);
- let node = widgetNode(startCount);
- assert.ok(node, "widget node at index");
- assert.equal(node.tagName, "toolbaritem", "widget element is correct");
- assert.equal(widget.width + "px", node.style.minWidth, "widget width is correct");
- assert.equal(widgetCount(), startCount + 1, "container has correct number of child elements");
- let content = node.firstElementChild;
- assert.ok(content, "found content");
- assert.ok(/iframe|image/.test(content.tagName), "content is iframe or image");
- return widget;
- }
- // Array of widgets to test
- // and a function to test them.
- let tests = [];
- function nextTest() {
- assert.equal(widgetCount(), 0, "widget in last test property cleaned itself up");
- if (!tests.length)
- done();
- else
- timer.setTimeout(tests.shift(), 0);
- }
- function doneTest() nextTest();
- // text widget
- tests.push(function testTextWidget() testSingleWidget({
- id: "text-single",
- label: "text widget",
- content: "oh yeah",
- contentScript: "self.postMessage(document.body.innerHTML);",
- contentScriptWhen: "end",
- onMessage: function (message) {
- assert.equal(this.content, message, "content matches");
- this.destroy();
- doneTest();
- }
- }));
- // html widget
- tests.push(function testHTMLWidget() testSingleWidget({
- id: "html",
- label: "html widget",
- content: "<div>oh yeah</div>",
- contentScript: "self.postMessage(document.body.innerHTML);",
- contentScriptWhen: "end",
- onMessage: function (message) {
- assert.equal(this.content, message, "content matches");
- this.destroy();
- doneTest();
- }
- }));
- // image url widget
- tests.push(function testImageURLWidget() testSingleWidget({
- id: "image",
- label: "image url widget",
- contentURL: fixtures.url("test.html"),
- contentScript: "self.postMessage({title: document.title, " +
- "tag: document.body.firstElementChild.tagName, " +
- "content: document.body.firstElementChild.innerHTML});",
- contentScriptWhen: "end",
- onMessage: function (message) {
- assert.equal(message.title, "foo", "title matches");
- assert.equal(message.tag, "P", "element matches");
- assert.equal(message.content, "bar", "element content matches");
- this.destroy();
- doneTest();
- }
- }));
- // web uri widget
- tests.push(function testWebURIWidget() testSingleWidget({
- id: "web",
- label: "web uri widget",
- contentURL: fixtures.url("test.html"),
- contentScript: "self.postMessage({title: document.title, " +
- "tag: document.body.firstElementChild.tagName, " +
- "content: document.body.firstElementChild.innerHTML});",
- contentScriptWhen: "end",
- onMessage: function (message) {
- assert.equal(message.title, "foo", "title matches");
- assert.equal(message.tag, "P", "element matches");
- assert.equal(message.content, "bar", "element content matches");
- this.destroy();
- doneTest();
- }
- }));
- // event: onclick + content
- tests.push(function testOnclickEventContent() testSingleWidget({
- id: "click-content",
- label: "click test widget - content",
- content: "<div id='me'>foo</div>",
- contentScript: "var evt = new MouseEvent('click', {button: 0});" +
- "document.getElementById('me').dispatchEvent(evt);",
- contentScriptWhen: "end",
- onClick: function() {
- assert.pass("onClick called");
- this.destroy();
- doneTest();
- }
- }));
- // event: onmouseover + content
- tests.push(function testOnmouseoverEventContent() testSingleWidget({
- id: "mouseover-content",
- label: "mouseover test widget - content",
- content: "<div id='me'>foo</div>",
- contentScript: "var evt = new MouseEvent('mouseover'); " +
- "document.getElementById('me').dispatchEvent(evt);",
- contentScriptWhen: "end",
- onMouseover: function() {
- assert.pass("onMouseover called");
- this.destroy();
- doneTest();
- }
- }));
- // event: onmouseout + content
- tests.push(function testOnmouseoutEventContent() testSingleWidget({
- id: "mouseout-content",
- label: "mouseout test widget - content",
- content: "<div id='me'>foo</div>",
- contentScript: "var evt = new MouseEvent('mouseout');" +
- "document.getElementById('me').dispatchEvent(evt);",
- contentScriptWhen: "end",
- onMouseout: function() {
- assert.pass("onMouseout called");
- this.destroy();
- doneTest();
- }
- }));
- // event: onclick + image
- tests.push(function testOnclickEventImage() testSingleWidget({
- id: "click-image",
- label: "click test widget - image",
- contentURL: fixtures.url("moz_favicon.ico"),
- contentScript: "var evt = new MouseEvent('click'); " +
- "document.body.firstElementChild.dispatchEvent(evt);",
- contentScriptWhen: "end",
- onClick: function() {
- assert.pass("onClick called");
- this.destroy();
- doneTest();
- }
- }));
- // event: onmouseover + image
- tests.push(function testOnmouseoverEventImage() testSingleWidget({
- id: "mouseover-image",
- label: "mouseover test widget - image",
- contentURL: fixtures.url("moz_favicon.ico"),
- contentScript: "var evt = new MouseEvent('mouseover');" +
- "document.body.firstElementChild.dispatchEvent(evt);",
- contentScriptWhen: "end",
- onMouseover: function() {
- assert.pass("onMouseover called");
- this.destroy();
- doneTest();
- }
- }));
- // event: onmouseout + image
- tests.push(function testOnmouseoutEventImage() testSingleWidget({
- id: "mouseout-image",
- label: "mouseout test widget - image",
- contentURL: fixtures.url("moz_favicon.ico"),
- contentScript: "var evt = new MouseEvent('mouseout'); " +
- "document.body.firstElementChild.dispatchEvent(evt);",
- contentScriptWhen: "end",
- onMouseout: function() {
- assert.pass("onMouseout called");
- this.destroy();
- doneTest();
- }
- }));
- // test multiple widgets
- tests.push(function testMultipleWidgets() {
- let w1 = widgets.Widget({id: "first", label: "first widget", content: "first content"});
- let w2 = widgets.Widget({id: "second", label: "second widget", content: "second content"});
- w1.destroy();
- w2.destroy();
- doneTest();
- });
- // test updating widget content
- let loads = 0;
- tests.push(function testUpdatingWidgetContent() testSingleWidget({
- id: "content-updating",
- label: "content update test widget",
- content: "<div id='me'>foo</div>",
- contentScript: "self.postMessage(1)",
- contentScriptWhen: "ready",
- onMessage: function(message) {
- if (!this.flag) {
- this.content = "<div id='me'>bar</div>";
- this.flag = 1;
- }
- else {
- assert.equal(this.content, "<div id='me'>bar</div>", 'content is as expected');
- this.destroy();
- doneTest();
- }
- }
- }));
- // test updating widget contentURL
- let url1 = "data:text/html;charset=utf-8,<body>foodle</body>";
- let url2 = "data:text/html;charset=utf-8,<body>nistel</body>";
- tests.push(function testUpdatingContentURL() testSingleWidget({
- id: "content-url-updating",
- label: "content update test widget",
- contentURL: url1,
- contentScript: "self.postMessage(document.location.href);",
- contentScriptWhen: "end",
- onMessage: function(message) {
- if (!this.flag) {
- assert.equal(this.contentURL.toString(), url1);
- assert.equal(message, url1);
- this.contentURL = url2;
- this.flag = 1;
- }
- else {
- assert.equal(this.contentURL.toString(), url2);
- assert.equal(message, url2);
- this.destroy();
- doneTest();
- }
- }
- }));
- // test tooltip
- tests.push(function testTooltip() testSingleWidget({
- id: "text-with-tooltip",
- label: "text widget",
- content: "oh yeah",
- tooltip: "foo",
- contentScript: "self.postMessage(1)",
- contentScriptWhen: "ready",
- onMessage: function(message) {
- assert.equal(this.tooltip, "foo", "tooltip matches");
- this.destroy();
- doneTest();
- }
- }));
- // test tooltip fallback to label
- tests.push(function testTooltipFallback() testSingleWidget({
- id: "fallback",
- label: "fallback",
- content: "oh yeah",
- contentScript: "self.postMessage(1)",
- contentScriptWhen: "ready",
- onMessage: function(message) {
- assert.equal(this.tooltip, this.label, "tooltip fallbacks to label");
- this.destroy();
- doneTest();
- }
- }));
- // test updating widget tooltip
- let updated = false;
- tests.push(function testUpdatingTooltip() testSingleWidget({
- id: "tooltip-updating",
- label: "tooltip update test widget",
- tooltip: "foo",
- content: "<div id='me'>foo</div>",
- contentScript: "self.postMessage(1)",
- contentScriptWhen: "ready",
- onMessage: function(message) {
- this.tooltip = "bar";
- assert.equal(this.tooltip, "bar", "tooltip gets updated");
- this.destroy();
- doneTest();
- }
- }));
- // test allow attribute
- tests.push(function testDefaultAllow() testSingleWidget({
- id: "allow-default",
- label: "allow.script attribute",
- content: "<script>document.title = 'ok';</script>",
- contentScript: "self.postMessage(document.title)",
- onMessage: function(message) {
- assert.equal(message, "ok", "scripts are evaluated by default");
- this.destroy();
- doneTest();
- }
- }));
- tests.push(function testExplicitAllow() testSingleWidget({
- id: "allow-explicit",
- label: "allow.script attribute",
- allow: {script: true},
- content: "<script>document.title = 'ok';</script>",
- contentScript: "self.postMessage(document.title)",
- onMessage: function(message) {
- assert.equal(message, "ok", "scripts are evaluated when we want to");
- this.destroy();
- doneTest();
- }
- }));
- tests.push(function testExplicitDisallow() testSingleWidget({
- id: "allow-explicit-disallow",
- label: "allow.script attribute",
- content: "<script>document.title = 'ok';</script>",
- allow: {script: false},
- contentScript: "self.postMessage(document.title)",
- onMessage: function(message) {
- assert.notEqual(message, "ok", "scripts aren't evaluated when " +
- "explicitly blocked it");
- this.destroy();
- doneTest();
- }
- }));
- // test multiple windows
- tests.push(function testMultipleWindows() {
- console.log('executing test multiple windows');
- openNewWindowTab("about:blank", { inNewWindow: true, onLoad: function(e) {
- let browserWindow = e.target.defaultView;
- assert.ok(browserWindow, 'window was opened');
- let doc = browserWindow.document;
- function container() australis ? doc.getElementById("nav-bar") : doc.getElementById("addon-bar");
- function widgetCount2() container() ? container().querySelectorAll('[id^="widget\:"]').length : 0;
- let widgetStartCount2 = widgetCount2();
- let w1Opts = {id:"first-multi-window", label: "first widget", content: "first content"};
- let w1 = testSingleWidget(w1Opts);
- assert.equal(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first widget");
- let w2Opts = {id:"second-multi-window", label: "second widget", content: "second content"};
- let w2 = testSingleWidget(w2Opts);
- assert.equal(widgetCount2(), widgetStartCount2 + 2, "2nd window has correct number of child elements after second widget");
- w1.destroy();
- assert.equal(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first destroy");
- w2.destroy();
- assert.equal(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after second destroy");
- close(browserWindow).then(doneTest);
- }});
- });
- // test window closing
- tests.push(function testWindowClosing() {
- // 1/ Create a new widget
- let w1Opts = {
- id:"first-win-closing",
- label: "first widget",
- content: "first content",
- contentScript: "self.port.on('event', function () self.port.emit('event'))"
- };
- let widget = testSingleWidget(w1Opts);
- let windows = require("sdk/windows").browserWindows;
- // 2/ Retrieve a WidgetView for the initial browser window
- let acceptDetach = false;
- let mainView = widget.getView(windows.activeWindow);
- assert.ok(mainView, "Got first widget view");
- mainView.on("detach", function () {
- // 8/ End of our test. Accept detach event only when it occurs after
- // widget.destroy()
- if (acceptDetach)
- doneTest();
- else
- assert.fail("View on initial window should not be destroyed");
- });
- mainView.port.on("event", function () {
- // 7/ Receive event sent during 6/ and cleanup our test
- acceptDetach = true;
- widget.destroy();
- });
- // 3/ First: open a new browser window
- windows.open({
- url: "about:blank",
- onOpen: function(window) {
- // 4/ Retrieve a WidgetView for this new window
- let view = widget.getView(window);
- assert.ok(view, "Got second widget view");
- view.port.on("event", function () {
- assert.fail("We should not receive event on the detach view");
- });
- view.on("detach", function () {
- // The related view is destroyed
- // 6/ Send a custom event
- assert.throws(function () {
- view.port.emit("event");
- },
- /^The widget has been destroyed and can no longer be used.$/,
- "emit on a destroyed view should throw");
- widget.port.emit("event");
- });
- // 5/ Destroy this window
- window.close();
- }
- });
- });
- if (false) {
- tests.push(function testAddonBarHide() {
- // Hide the addon-bar
- browserWindow.setToolbarVisibility(container(), false);
- assert.ok(container().collapsed,
- "1st window starts with an hidden addon-bar");
- // Then open a browser window and verify that the addon-bar remains hidden
- openNewWindowTab("about:blank", { inNewWindow: true, onLoad: function(e) {
- let browserWindow2 = e.target.defaultView;
- let doc2 = browserWindow2.document;
- function container2() doc2.getElementById("addon-bar");
- function widgetCount2() container2() ? container2().childNodes.length : 0;
- let widgetStartCount2 = widgetCount2();
- assert.ok(container2().collapsed,
- "2nd window starts with an hidden addon-bar");
- let w1Opts = {id:"first-addonbar-hide", label: "first widget", content: "first content"};
- let w1 = testSingleWidget(w1Opts);
- assert.equal(widgetCount2(), widgetStartCount2 + 1,
- "2nd window has correct number of child elements after" +
- "widget creation");
- assert.ok(!container().collapsed, "1st window has a visible addon-bar");
- assert.ok(!container2().collapsed, "2nd window has a visible addon-bar");
- w1.destroy();
- assert.equal(widgetCount2(), widgetStartCount2,
- "2nd window has correct number of child elements after" +
- "widget destroy");
- assert.ok(container().collapsed, "1st window has an hidden addon-bar");
- assert.ok(container2().collapsed, "2nd window has an hidden addon-bar");
- // Reset addon-bar visibility before exiting this test
- browserWindow.setToolbarVisibility(container(), true);
- close(browserWindow2).then(doneTest);
- }});
- });
- }
- // test widget.width
- tests.push(function testWidgetWidth() testSingleWidget({
- id: "text-test-width",
- label: "test widget.width",
- content: "test width",
- width: 200,
- contentScript: "self.postMessage(1)",
- contentScriptWhen: "ready",
- onMessage: function(message) {
- assert.equal(this.width, 200, 'width is 200');
- let node = widgetNode(0);
- assert.equal(this.width, node.style.minWidth.replace("px", ""));
- assert.equal(this.width, node.firstElementChild.style.width.replace("px", ""));
- this.width = 300;
- assert.equal(this.width, node.style.minWidth.replace("px", ""));
- assert.equal(this.width, node.firstElementChild.style.width.replace("px", ""));
- this.destroy();
- doneTest();
- }
- }));
- // test click handler not respond to right-click
- let clickCount = 0;
- tests.push(function testNoRightClick() testSingleWidget({
- id: "right-click-content",
- label: "click test widget - content",
- content: "<div id='me'>foo</div>",
- contentScript: // Left click
- "var evt = new MouseEvent('click', {button: 0});" +
- "document.getElementById('me').dispatchEvent(evt); " +
- // Middle click
- "evt = new MouseEvent('click', {button: 1});" +
- "document.getElementById('me').dispatchEvent(evt); " +
- // Right click
- "evt = new MouseEvent('click', {button: 2});" +
- "document.getElementById('me').dispatchEvent(evt); " +
- // Mouseover
- "evt = new MouseEvent('mouseover');" +
- "document.getElementById('me').dispatchEvent(evt);",
- contentScriptWhen: "end",
- onClick: function() clickCount++,
- onMouseover: function() {
- assert.equal(clickCount, 1, "only left click was sent to click handler");
- this.destroy();
- doneTest();
- }
- }));
- // kick off test execution
- doneTest();
- };
- exports.testWidgetWithValidPanel = function(assert, done) {
- const widgets = require("sdk/widget");
- let widget1 = widgets.Widget({
- id: "testWidgetWithValidPanel",
- label: "panel widget 1",
- content: "<div id='me'>foo</div>",
- contentScript: "var evt = new MouseEvent('click', {button: 0});" +
- "document.body.dispatchEvent(evt);",
- contentScriptWhen: "end",
- panel: require("sdk/panel").Panel({
- contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
- onShow: function() {
- let { document } = getMostRecentBrowserWindow();
- let widgetEle = document.getElementById("widget:" + jetpackID + "-" + widget1.id);
- let panelEle = document.getElementById('mainPopupSet').lastChild;
- // See bug https://bugzilla.mozilla.org/show_bug.cgi?id=859592
- assert.equal(panelEle.getAttribute("type"), "arrow", 'the panel is a arrow type');
- assert.strictEqual(panelEle.anchorNode, widgetEle, 'the panel is properly anchored to the widget');
- widget1.destroy();
- assert.pass("panel displayed on click");
- done();
- }
- })
- });
- };
- exports.testWidgetWithInvalidPanel = function(assert) {
- const widgets = require("sdk/widget");
- assert.throws(
- function() {
- widgets.Widget({
- id: "panel2",
- label: "panel widget 2",
- panel: {}
- });
- },
- /^The option \"panel\" must be one of the following types: null, undefined, object$/,
- "widget.panel must be a Panel object");
- };
- exports.testPanelWidget3 = function testPanelWidget3(assert, done) {
- const widgets = require("sdk/widget");
- let onClickCalled = false;
- let widget3 = widgets.Widget({
- id: "panel3",
- label: "panel widget 3",
- content: "<div id='me'>foo</div>",
- contentScript: "var evt = new MouseEvent('click', {button: 0});" +
- "document.body.firstElementChild.dispatchEvent(evt);",
- contentScriptWhen: "end",
- onClick: function() {
- onClickCalled = true;
- this.panel.show();
- },
- panel: require("sdk/panel").Panel({
- contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
- onShow: function() {
- assert.ok(
- onClickCalled,
- "onClick called on click for widget with both panel and onClick");
- widget3.destroy();
- done();
- }
- })
- });
- };
- exports.testWidgetWithPanelInMenuPanel = function(assert, done) {
- let CustomizableUI;
- try {
- ({CustomizableUI}) = Cu.import("resource:///modules/CustomizableUI.jsm", {});
- }
- catch (e) {
- assert.pass("Test skipped: no CustomizableUI object found.");
- done();
- return;
- }
- const widgets = require("sdk/widget");
- let widget1 = widgets.Widget({
- id: "panel1",
- label: "panel widget 1",
- content: "<div id='me'>foo</div>",
- contentScript: "new " + function() {
- self.port.on('click', () => {
- let evt = new MouseEvent('click', {button: 0});
- document.body.dispatchEvent(evt);
- });
- },
- contentScriptWhen: "end",
- panel: require("sdk/panel").Panel({
- contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
- onShow: function() {
- let { document } = getMostRecentBrowserWindow();
- let { anchorNode } = document.getElementById('mainPopupSet').lastChild;
- let panelButtonNode = document.getElementById("PanelUI-menu-button");
- assert.strictEqual(anchorNode, panelButtonNode,
- 'the panel is anchored to the panel menu button instead of widget');
- widget1.destroy();
- done();
- }
- })
- });
- let widgetId = "widget:" + jetpackID + "-" + widget1.id;
- CustomizableUI.addListener({
- onWidgetAdded: function(id) {
- if (id !== widgetId) return;
- let { document, PanelUI } = getMostRecentBrowserWindow();
- PanelUI.panel.addEventListener('popupshowing', function onshow({type}) {
- this.removeEventListener(type, onshow);
- widget1.port.emit('click');
- });
- document.getElementById("PanelUI-menu-button").click()
- }
- });
- CustomizableUI.addWidgetToArea(widgetId, CustomizableUI.AREA_PANEL);
- };
- exports.testWidgetMessaging = function testWidgetMessaging(assert, done) {
- let origMessage = "foo";
- const widgets = require("sdk/widget");
- let widget = widgets.Widget({
- id: "widget-messaging",
- label: "foo",
- content: "<bar>baz</bar>",
- contentScriptWhen: "end",
- contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');",
- onMessage: function(message) {
- if (message == "ready")
- widget.postMessage(origMessage);
- else {
- assert.equal(origMessage, message);
- widget.destroy();
- done();
- }
- }
- });
- };
- exports.testWidgetViews = function testWidgetViews(assert, done) {
- const widgets = require("sdk/widget");
- let widget = widgets.Widget({
- id: "widget-views",
- label: "foo",
- content: "<bar>baz</bar>",
- contentScriptWhen: "ready",
- contentScript: "self.on('message', function(data) self.postMessage(data)); self.postMessage('ready')",
- onAttach: function(view) {
- assert.pass("WidgetView created");
- view.on("message", function () {
- assert.pass("Got message in WidgetView");
- widget.destroy();
- });
- view.on("detach", function () {
- assert.pass("WidgetView destroyed");
- done();
- });
- }
- });
- };
- exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(assert, done) {
- const widgets = require("sdk/widget");
- let view = null;
- let widget = widgets.Widget({
- id: "widget-view-ui-events",
- label: "foo",
- content: "<div id='me'>foo</div>",
- contentScript: "var evt = new MouseEvent('click', {button: 0});" +
- "document.getElementById('me').dispatchEvent(evt);",
- contentScriptWhen: "ready",
- onAttach: function(attachView) {
- view = attachView;
- assert.pass("Got attach event");
- },
- onClick: function (eventView) {
- assert.equal(view, eventView,
- "event first argument is equal to the WidgetView");
- let view2 = widget.getView(require("sdk/windows").browserWindows.activeWindow);
- assert.equal(view, view2,
- "widget.getView return the same WidgetView");
- widget.destroy();
- done();
- }
- });
- };
- exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(assert, done) {
- const widgets = require("sdk/widget");
- let widget = widgets.Widget({
- id: "widget-view-custom-events",
- label: "foo",
- content: "<div id='me'>foo</div>",
- contentScript: "self.port.emit('event', 'ok');",
- contentScriptWhen: "ready",
- onAttach: function(view) {
- view.port.on("event", function (data) {
- assert.equal(data, "ok",
- "event argument is valid on WidgetView");
- });
- },
- });
- widget.port.on("event", function (data) {
- assert.equal(data, "ok", "event argument is valid on Widget");
- widget.destroy();
- done();
- });
- };
- exports.testWidgetViewsTooltip = function testWidgetViewsTooltip(assert, done) {
- const widgets = require("sdk/widget");
- let widget = new widgets.Widget({
- id: "widget-views-tooltip",
- label: "foo",
- content: "foo"
- });
- let view = widget.getView(require("sdk/windows").browserWindows.activeWindow);
- widget.tooltip = null;
- assert.equal(view.tooltip, "foo",
- "view tooltip defaults to base widget label");
- assert.equal(widget.tooltip, "foo",
- "tooltip defaults to base widget label");
- widget.destroy();
- done();
- };
- exports.testWidgetMove = function testWidgetMove(assert, done) {
- let windowUtils = require("sdk/deprecated/window-utils");
- let widgets = require("sdk/widget");
- let browserWindow = windowUtils.activeBrowserWindow;
- let doc = browserWindow.document;
- let label = "unique-widget-label";
- let origMessage = "message after node move";
- let gotFirstReady = false;
- let widget = widgets.Widget({
- id: "widget-move",
- label: label,
- content: "<bar>baz</bar>",
- contentScriptWhen: "ready",
- contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');",
- onMessage: function(message) {
- if (message == "ready") {
- if (!gotFirstReady) {
- assert.pass("Got first ready event");
- let widgetNode = doc.querySelector('toolbaritem[label="' + label + '"]');
- let parent = widgetNode.parentNode;
- parent.insertBefore(widgetNode, parent.firstChild);
- gotFirstReady = true;
- }
- else {
- assert.pass("Got second ready event");
- widget.postMessage(origMessage);
- }
- }
- else {
- assert.equal(origMessage, message, "Got message after node move");
- widget.destroy();
- done();
- }
- }
- });
- };
- /*
- The bug is exhibited when a widget with HTML content has it's content
- changed to new HTML content with a pound in it. Because the src of HTML
- content is converted to a data URI, the underlying iframe doesn't
- consider the content change a navigation change, so doesn't load
- the new content.
- */
- exports.testWidgetWithPound = function testWidgetWithPound(assert, done) {
- function getWidgetContent(widget) {
- let windowUtils = require("sdk/deprecated/window-utils");
- let browserWindow = windowUtils.activeBrowserWindow;
- let doc = browserWindow.document;
- let widgetNode = doc.querySelector('toolbaritem[label="' + widget.label + '"]');
- assert.ok(widgetNode, 'found widget node in the front-end');
- return widgetNode.firstChild.contentDocument.body.innerHTML;
- }
- let widgets = require("sdk/widget");
- let count = 0;
- let widget = widgets.Widget({
- id: "1",
- label: "foo",
- content: "foo",
- contentScript: "window.addEventListener('load', self.postMessage, false);",
- onMessage: function() {
- count++;
- if (count == 1) {
- widget.content = "foo#";
- }
- else {
- assert.equal(getWidgetContent(widget), "foo#", "content updated to pound?");
- widget.destroy();
- done();
- }
- }
- });
- };
- exports.testContentScriptOptionsOption = function(assert, done) {
- let widget = require("sdk/widget").Widget({
- id: "widget-script-options",
- label: "fooz",
- content: "fooz",
- contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
- contentScriptWhen: "end",
- contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
- 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' );
- widget.destroy();
- done();
- }
- });
- };
- exports.testOnAttachWithoutContentScript = function(assert, done) {
- let widget = require("sdk/widget").Widget({
- id: "onAttachNoCS",
- label: "onAttachNoCS",
- content: "onAttachNoCS",
- onAttach: function (view) {
- assert.pass("received attach event");
- widget.destroy();
- done();
- }
- });
- };
- exports.testPostMessageOnAttach = function(assert, done) {
- let widget = require("sdk/widget").Widget({
- id: "onAttach",
- label: "onAttach",
- content: "onAttach",
- // 1) Send a message immediatly after `attach` event
- onAttach: function (view) {
- view.postMessage("ok");
- },
- // 2) Listen to it and forward it back to the widget
- contentScript: "self.on('message', self.postMessage);",
- // 3) Listen to this forwarded message
- onMessage: function (msg) {
- assert.equal( msg, "ok", "postMessage works on `attach` event");
- widget.destroy();
- done();
- }
- });
- };
- exports.testPostMessageOnLocationChange = function(assert, done) {
- let attachEventCount = 0;
- let messagesCount = 0;
- let widget = require("sdk/widget").Widget({
- id: "onLocationChange",
- label: "onLocationChange",
- content: "onLocationChange",
- contentScript: "new " + function ContentScriptScope() {
- // Emit an event when content script is applied in order to know when
- // the first document is loaded so that we can load the 2nd one
- self.postMessage("ready");
- // And forward any incoming message back to the widget to see if
- // messaging is working on 2nd document
- self.on("message", self.postMessage);
- },
- onMessage: function (msg) {
- messagesCount++;
- if (messagesCount == 1) {
- assert.equal(msg, "ready", "First document is loaded");
- widget.content = "location changed";
- }
- else if (messagesCount == 2) {
- assert.equal(msg, "ready", "Second document is loaded");
- widget.postMessage("ok");
- }
- else if (messagesCount == 3) {
- assert.equal(msg, "ok",
- "We receive the message sent to the 2nd document");
- widget.destroy();
- done();
- }
- }
- });
- };
- exports.testSVGWidget = function(assert, done) {
- // use of capital SVG here is intended, that was failing..
- let SVG_URL = fixtures.url("mofo_logo.SVG");
- let widget = require("sdk/widget").Widget({
- id: "mozilla-svg-logo",
- label: "moz foundation logo",
- contentURL: SVG_URL,
- contentScript: "self.postMessage({count: window.document.images.length, src: window.document.images[0].src});",
- onMessage: function(data) {
- widget.destroy();
- assert.equal(data.count, 1, 'only one image');
- assert.equal(data.src, SVG_URL, 'only one image');
- done();
- }
- });
- };
- exports.testReinsertion = function(assert, done) {
- const WIDGETID = "test-reinsertion";
- let windowUtils = require("sdk/deprecated/window-utils");
- let browserWindow = windowUtils.activeBrowserWindow;
- let widget = require("sdk/widget").Widget({
- id: "test-reinsertion",
- label: "test reinsertion",
- content: "Test",
- });
- let realWidgetId = "widget:" + jetpackID + "-" + WIDGETID;
- // Remove the widget:
- if (australis) {
- browserWindow.CustomizableUI.removeWidgetFromArea(realWidgetId);
- } else {
- let widget = browserWindow.document.getElementById(realWidgetId);
- let container = widget.parentNode;
- container.currentSet = container.currentSet.replace("," + realWidgetId, "");
- container.setAttribute("currentset", container.currentSet);
- container.ownerDocument.persist(container.id, "currentset");
- }
- openNewWindowTab("about:blank", { inNewWindow: true, onLoad: function(e) {
- assert.equal(e.target.defaultView.document.getElementById(realWidgetId), null);
- close(e.target.defaultView).then(done);
- }});
- };
- if (!australis) {
- exports.testNavigationBarWidgets = function testNavigationBarWidgets(assert, done) {
- let w1 = widgets.Widget({id: "1st", label: "1st widget", content: "1"});
- let w2 = widgets.Widget({id: "2nd", label: "2nd widget", content: "2"});
- let w3 = widgets.Widget({id: "3rd", label: "3rd widget", content: "3"});
- // First wait for all 3 widgets to be added to the current browser window
- let firstAttachCount = 0;
- function onAttachFirstWindow(widget) {
- if (++firstAttachCount<3)
- return;
- onWidgetsReady();
- }
- w1.once("attach", onAttachFirstWindow);
- w2.once("attach", onAttachFirstWindow);
- w3.once("attach", onAttachFirstWindow);
- function getWidgetNode(toolbar, position) {
- return toolbar.getElementsByTagName("toolbaritem")[position];
- }
- function openBrowserWindow() {
- let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
- getService(Ci.nsIWindowWatcher);
- let urlString = Cc["@mozilla.org/supports-string;1"].
- createInstance(Ci.nsISupportsString);
- urlString.data = "about:blank";
- return ww.openWindow(null, "chrome://browser/content/browser.xul",
- "_blank", "chrome,all,dialog=no", urlString);
- }
- // Then move them before openeing a new browser window
- function onWidgetsReady() {
- // Hack to move 2nd and 3rd widgets manually to the navigation bar right after
- // the search box.
- let browserWindow = windowUtils.activeBrowserWindow;
- let doc = browserWindow.document;
- let addonBar = doc.getElementById("addon-bar");
- let w2ToolbarItem = getWidgetNode(addonBar, 1);
- let w3ToolbarItem = getWidgetNode(addonBar, 2);
- let navBar = doc.getElementById("nav-bar");
- let searchBox = doc.getElementById("search-container");
- // Insert 3rd at the right of search box by adding it before its right sibling
- navBar.insertItem(w3ToolbarItem.id, searchBox.nextSibling, null, false);
- // Then insert 2nd before 3rd
- navBar.insertItem(w2ToolbarItem.id, w3ToolbarItem, null, false);
- // Widget and Firefox codes rely on this `currentset` attribute,
- // so ensure it is correctly saved
- navBar.setAttribute("currentset", navBar.currentSet);
- doc.persist(navBar.id, "currentset");
- // Update addonbar too as we removed widget from there.
- // Otherwise, widgets may still be added to this toolbar.
- addonBar.setAttribute("currentset", addonBar.currentSet);
- doc.persist(addonBar.id, "currentset");
- // Wait for all widget to be attached to this new window before checking
- // their position
- let attachCount = 0;
- let browserWindow2;
- function onAttach(widget) {
- if (++attachCount < 3)
- return;
- let doc = browserWindow2.document;
- let addonBar = doc.getElementById("addon-bar");
- let searchBox = doc.getElementById("search-container");
- // Ensure that 1st is in addon bar
- assert.equal(getWidgetNode(addonBar, 0).getAttribute("label"), w1.label);
- // And that 2nd and 3rd keep their original positions in navigation bar,
- // i.e. right after search box
- assert.equal(searchBox.nextSibling.getAttribute("label"), w2.label);
- assert.equal(searchBox.nextSibling.nextSibling.getAttribute("label"), w3.label);
- w1.destroy();
- w2.destroy();
- w3.destroy();
- close(browserWindow2).then(done);
- }
- w1.on("attach", onAttach);
- w2.on("attach", onAttach);
- w3.on("attach", onAttach);
- browserWindow2 = openBrowserWindow(browserWindow);
- }
- };
- }
- require("sdk/test").run(exports);
|