/* 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 { Cu } = require("chrome"); const { Frame } = require("sdk/ui/frame"); const { Toolbar } = require("sdk/ui/toolbar"); const { Loader } = require("sdk/test/loader"); const { identify } = require("sdk/ui/id"); const { setTimeout } = require("sdk/timers"); const { getMostRecentBrowserWindow, open } = require("sdk/window/utils"); const { ready, loaded, close } = require("sdk/window/helpers"); const { defer, all } = require("sdk/core/promise"); const { send } = require("sdk/event/utils"); const { object } = require("sdk/util/sequence"); const { OutputPort } = require("sdk/output/system"); const { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); const output = new OutputPort({ id: "toolbar-change" }); const wait = (toolbar, event, times) => { let { promise, resolve } = defer(); if (times) { let resolveArray = []; let counter = 0; toolbar.on(event, function onEvent (e) { resolveArray.push(e); if (++counter === times) { toolbar.off(event, onEvent); resolve(resolveArray); } }); } else { toolbar.once(event, resolve); } return promise; }; const stateEventsFor = frame => [wait(frame, "attach"), wait(frame, "ready"), wait(frame, "load"), wait(frame, "detach")]; const isAttached = ({id}, window=getMostRecentBrowserWindow()) => !!window.document.getElementById(id); // Use `Task.spawn` instead of `Task.async` because the returned function does not contain // a length for the test harness to determine whether the test should be executed exports["test frame API"] = function* (assert) { const url = "data:text/html,frame-api"; assert.throws(() => new Frame(), /The `options.url`/, "must provide url"); assert.throws(() => new Frame({ url: "http://mozilla.org/" }), /The `options.url`/, "options.url must be local url"); assert.throws(() => new Frame({url: url, name: "4you" }), /The `option.name` must be a valid/, "can only take valid names"); const f1 = new Frame({ url: url }); assert.ok(f1.id, "frame has an id"); assert.equal(f1.url, void(0), "frame has no url until it's loaded"); assert.equal(typeof(f1.postMessage), "function", "frames can postMessages"); const p1 = wait(f1, "register"); assert.throws(() => new Frame({ url: url }), /Frame with this id already exists/, "can't have two identical frames"); const f2 = new Frame({ name: "frame-2", url: url }); assert.pass("can create frame with same url but diff name"); const p2 = wait(f2, "register"); yield p1; assert.pass("frame#1 was registered"); assert.equal(f1.url, url, "once registered it get's url"); yield p2; assert.pass("frame#2 was registered"); assert.equal(f2.url, url, "once registered it get's url"); f1.destroy(); const f3 = new Frame({ url: url }); assert.pass("frame identical to destroyed one can be created"); yield wait(f3, "register"); assert.equal(f3.url, url, "url is set"); f2.destroy(); f3.destroy(); }; exports["test frame in toolbar"] = function* (assert) { const assertEvent = (event, type) => { assert.ok(event, "`" + type + "` event was dispatched"); assert.equal(event.type, type, "event.type is: " + type); assert.equal(typeof(event.source), "object", "event.source is an object"); assert.equal(typeof(event.source.postMessage), "function", "messages can be posted to event.source"); }; const url = "data:text/html,toolbar-frame"; const f1 = new Frame({ url: url }); const t1 = new Toolbar({ title: "frame toolbar", items: [f1] }); const w1 = getMostRecentBrowserWindow(); const [a1, r1, l1] = stateEventsFor(f1); assertEvent((yield a1), "attach"); assert.ok(isAttached(f1, w1), "frame is in the window#1"); assertEvent((yield r1), "ready"); assertEvent((yield l1), "load"); const [a2, r2, l2] = stateEventsFor(f1); const w2 = open(); assertEvent((yield a2), "attach"); assert.ok(isAttached(f1, w2), "frame is in the window#2"); assertEvent((yield r2), "ready"); assertEvent((yield l2), "load"); assert.pass("frame attached to window#2"); const d1 = wait(f1, "detach"); yield close(w2); assertEvent((yield d1), "detach"); assert.pass("frame detached when window is closed"); t1.destroy(); assertEvent((yield wait(f1, "detach")), "detach"); assert.ok(!isAttached(f1, w1), "frame was removed from window#1"); assert.pass("toolbar destroy detaches frame"); }; exports["test host to content messaging"] = function* (assert) { const url = "data:text/html,"; const f1 = new Frame({ name: "mailbox", url: url }); const t1 = new Toolbar({ title: "mailbox", items: [f1] }); const e1 = yield wait(f1, "ready"); e1.source.postMessage("ping!", e1.origin); const pong = yield wait(f1, "message"); assert.equal(pong.data, "pong!", "received ping back"); t1.destroy(); yield wait(t1, "detach"); }; exports["test content to host messaging"] = function* (assert) { const url = "data:text/html,"; const f1 = new Frame({ name: "inbox", url: url }); const t1 = new Toolbar({ title: "inbox", items: [f1] }); const e1 = yield wait(f1, "message"); assert.equal(e1.data, "ping!", "received ping from content"); e1.source.postMessage("pong!", e1.origin); const e2 = yield wait(f1, "message"); assert.equal(e2.data, "end", "received end message"); t1.destroy(); yield wait(t1, "detach"); }; exports["test direct messaging"] = function* (assert) { let message; const url = "data:text/html,"; const w1 = getMostRecentBrowserWindow(); const f1 = new Frame({ url: url, name: "mail-cluster" }); const t1 = new Toolbar({ title: "claster", items: [f1] }); yield wait(f1, "ready"); assert.pass("document loaded in window#1"); const w2 = open(); yield wait(f1, "ready"); assert.pass("document loaded in window#2"); let messages = wait(f1, "message", 2); f1.postMessage("inc", f1.origin); f1.postMessage("print", f1.origin); const [e1, e2] = yield messages; assert.deepEqual(e1.data, {n: 1}, "received message from window#1"); assert.deepEqual(e2.data, {n: 1}, "received message from window#2"); message = wait(f1, "message"); e1.source.postMessage("inc", e1.origin); e1.source.postMessage("print", e1.origin); const e3 = yield message; assert.deepEqual(e3.data, {n: 2}, "state changed in window#1"); message = wait(f1, "message"); e2.source.postMessage("print", e2.origin); yield message; assert.deepEqual(e2.data, {n:1}, "window#2 didn't received inc message"); yield close(w2); t1.destroy(); yield wait(t1, "detach"); }; require("sdk/test").run(module.exports);