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