michael@0: /* globals Components: true, Promise: true, gBrowser: true, Test: true,
michael@0: info: true, is: true, window: true, waitForExplicitFinish: true,
michael@0: finish: true, ok: true*/
michael@0:
michael@0: "use strict";
michael@0:
michael@0: const { interfaces: Ci, classes: Cc, utils: Cu } = Components;
michael@0: const { addObserver, removeObserver } = Cc["@mozilla.org/observer-service;1"].
michael@0: getService(Ci.nsIObserverService);
michael@0: const { openWindow } = Cc["@mozilla.org/embedcomp/window-watcher;1"].
michael@0: getService(Ci.nsIWindowWatcher);
michael@0:
michael@0: const Test = routine => () => {
michael@0: waitForExplicitFinish();
michael@0: Task.spawn(routine)
michael@0: .then(finish, error => {
michael@0: ok(false, error);
michael@0: finish();
michael@0: });
michael@0: };
michael@0:
michael@0: // Returns promise for the observer notification subject for
michael@0: // the given topic. If `receive("foo")` is called `n` times
michael@0: // nth promise is resolved on an `nth` "foo" notification.
michael@0: const receive = (topic, p, syncCallback) => {
michael@0: const { promise, resolve, reject } = Promise.defer();
michael@0: const { queue } = receive;
michael@0: const timeout = () => {
michael@0: queue.splice(queue.indexOf(resolve) - 1, 2);
michael@0: reject(new Error("Timeout"));
michael@0: };
michael@0:
michael@0: const observer = {
michael@0: observe: subject => {
michael@0: // Browser loads bunch of other documents that we don't care
michael@0: // about so we let allow filtering notifications via `p` function.
michael@0: if (p && !p(subject)) return;
michael@0: // If observer is a first one with a given `topic`
michael@0: // in a queue resolve promise and take it off the queue
michael@0: // otherwise keep waiting.
michael@0: const index = queue.indexOf(topic);
michael@0: if (queue.indexOf(resolve) === index + 1) {
michael@0: removeObserver(observer, topic);
michael@0: clearTimeout(id, reject);
michael@0: queue.splice(index, 2);
michael@0: // Some tests need to be executed synchronously when the event is fired.
michael@0: if (syncCallback) {
michael@0: syncCallback(subject);
michael@0: }
michael@0: resolve(subject);
michael@0: }
michael@0: }
michael@0: };
michael@0: const id = setTimeout(timeout, 90000);
michael@0: addObserver(observer, topic, false);
michael@0: queue.push(topic, resolve);
michael@0:
michael@0: return promise;
michael@0: };
michael@0: receive.queue = [];
michael@0:
michael@0: const openTab = uri => gBrowser.selectedTab = gBrowser.addTab(uri);
michael@0:
michael@0: const sleep = ms => {
michael@0: const { promise, resolve } = Promise.defer();
michael@0: setTimeout(resolve, ms);
michael@0: return promise;
michael@0: };
michael@0:
michael@0: const isData = document => document.URL.startsWith("data:");
michael@0:
michael@0: const uri1 = "data:text/html;charset=utf-8,
1
";
michael@0: // For whatever reason going back on load event doesn't work so timeout it is :(
michael@0: const uri2 = "data:text/html;charset=utf-8,2
";
michael@0: const uri3 = "data:text/html;charset=utf-8,3
";
michael@0:
michael@0: const uri4 = "chrome://browser/content/license.html";
michael@0:
michael@0: const test = Test(function*() {
michael@0: let documentInteractive = receive("content-document-interactive", isData, d => {
michael@0: // This test is executed synchronously when the event is received.
michael@0: is(d.readyState, "interactive", "document is interactive");
michael@0: is(d.URL, uri1, "document.URL matches tab url");
michael@0: });
michael@0: let documentLoaded = receive("content-document-loaded", isData);
michael@0: let pageShown = receive("content-page-shown", isData);
michael@0:
michael@0: info("open: uri#1");
michael@0: const tab1 = openTab(uri1);
michael@0: const browser1 = gBrowser.getBrowserForTab(tab1);
michael@0:
michael@0: let interactiveDocument1 = yield documentInteractive;
michael@0:
michael@0: let loadedDocument1 = yield documentLoaded;
michael@0: is(loadedDocument1.readyState, "complete", "document is loaded");
michael@0: is(interactiveDocument1, loadedDocument1, "interactive document is loaded");
michael@0:
michael@0: let shownPage = yield pageShown;
michael@0: is(interactiveDocument1, shownPage, "loaded document is shown");
michael@0:
michael@0: // Wait until history entry is created before loading new uri.
michael@0: yield receive("sessionstore-state-write-complete");
michael@0:
michael@0: info("load uri#2");
michael@0:
michael@0: documentInteractive = receive("content-document-interactive", isData, d => {
michael@0: // This test is executed synchronously when the event is received.
michael@0: is(d.readyState, "interactive", "document is interactive");
michael@0: is(d.URL, uri2, "document.URL matches URL loaded");
michael@0: });
michael@0: documentLoaded = receive("content-document-loaded", isData);
michael@0: pageShown = receive("content-page-shown", isData);
michael@0: let pageHidden = receive("content-page-hidden", isData);
michael@0:
michael@0: browser1.loadURI(uri2);
michael@0:
michael@0: let hiddenPage = yield pageHidden;
michael@0: is(interactiveDocument1, hiddenPage, "loaded document is hidden");
michael@0:
michael@0: let interactiveDocument2 = yield documentInteractive;
michael@0:
michael@0: let loadedDocument2 = yield documentLoaded;
michael@0: is(loadedDocument2.readyState, "complete", "document is loaded");
michael@0: is(interactiveDocument2, loadedDocument2, "interactive document is loaded");
michael@0:
michael@0: shownPage = yield pageShown;
michael@0: is(interactiveDocument2, shownPage, "loaded document is shown");
michael@0:
michael@0: info("go back to uri#1");
michael@0:
michael@0:
michael@0: documentInteractive = receive("content-document-interactive", isData, d => {
michael@0: // This test is executed synchronously when the event is received.
michael@0: is(d.readyState, "interactive", "document is interactive");
michael@0: is(d.URL, uri3, "document.URL matches URL loaded");
michael@0: });
michael@0: documentLoaded = receive("content-document-loaded", isData);
michael@0: pageShown = receive("content-page-shown", isData);
michael@0: pageHidden = receive("content-page-hidden", isData);
michael@0:
michael@0: hiddenPage = yield pageHidden;
michael@0: is(interactiveDocument2, hiddenPage, "new document is hidden");
michael@0:
michael@0: shownPage = yield pageShown;
michael@0: is(interactiveDocument1, shownPage, "previous document is shown");
michael@0:
michael@0: info("load uri#3");
michael@0:
michael@0: browser1.loadURI(uri3);
michael@0:
michael@0: pageShown = receive("content-page-shown", isData);
michael@0:
michael@0: let interactiveDocument3 = yield documentInteractive;
michael@0:
michael@0: let loadedDocument3 = yield documentLoaded;
michael@0: is(loadedDocument3.readyState, "complete", "document is loaded");
michael@0: is(interactiveDocument3, loadedDocument3, "interactive document is loaded");
michael@0:
michael@0: shownPage = yield pageShown;
michael@0: is(interactiveDocument3, shownPage, "previous document is shown");
michael@0:
michael@0: gBrowser.removeTab(tab1);
michael@0:
michael@0: info("load chrome uri");
michael@0:
michael@0: const tab2 = openTab(uri4);
michael@0: documentInteractive = receive("chrome-document-interactive", null, d => {
michael@0: // This test is executed synchronously when the event is received.
michael@0: is(d.readyState, "interactive", "document is interactive");
michael@0: is(d.URL, uri4, "document.URL matches URL loaded");
michael@0: });
michael@0: documentLoaded = receive("chrome-document-loaded");
michael@0: pageShown = receive("chrome-page-shown");
michael@0:
michael@0: const interactiveDocument4 = yield documentInteractive;
michael@0:
michael@0: let loadedDocument4 = yield documentLoaded;
michael@0: is(loadedDocument4.readyState, "complete", "document is loaded");
michael@0: is(interactiveDocument4, loadedDocument4, "interactive document is loaded");
michael@0:
michael@0: shownPage = yield pageShown;
michael@0: is(interactiveDocument4, shownPage, "loaded chrome document is shown");
michael@0:
michael@0: pageHidden = receive("chrome-page-hidden");
michael@0: gBrowser.removeTab(tab2);
michael@0:
michael@0: hiddenPage = yield pageHidden;
michael@0: is(interactiveDocument4, hiddenPage, "chrome document hidden");
michael@0: });