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