browser_state_notifications.js (6886B)
1 /* globals Components: true, Promise: true, gBrowser: true, Test: true, 2 info: true, is: true, window: true, waitForExplicitFinish: true, 3 finish: true, ok: true*/ 4 5 "use strict"; 6 7 const { addObserver, removeObserver } = Cc[ 8 "@mozilla.org/observer-service;1" 9 ].getService(Ci.nsIObserverService); 10 const { openWindow } = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService( 11 Ci.nsIWindowWatcher 12 ); 13 14 const Test = routine => () => { 15 waitForExplicitFinish(); 16 routine().then(finish, error => { 17 ok(false, error); 18 finish(); 19 }); 20 }; 21 22 // Returns promise for the observer notification subject for 23 // the given topic. If `receive("foo")` is called `n` times 24 // nth promise is resolved on an `nth` "foo" notification. 25 const receive = (topic, p, syncCallback) => { 26 return new Promise((resolve, reject) => { 27 const { queue } = receive; 28 const timeout = () => { 29 queue.splice(queue.indexOf(resolve) - 1, 2); 30 reject(new Error("Timeout")); 31 }; 32 33 const observer = { 34 observe: subject => { 35 // Browser loads bunch of other documents that we don't care 36 // about so we let allow filtering notifications via `p` function. 37 if (p && !p(subject)) { 38 return; 39 } 40 // If observer is a first one with a given `topic` 41 // in a queue resolve promise and take it off the queue 42 // otherwise keep waiting. 43 const index = queue.indexOf(topic); 44 if (queue.indexOf(resolve) === index + 1) { 45 removeObserver(observer, topic); 46 clearTimeout(id, reject); 47 queue.splice(index, 2); 48 // Some tests need to be executed synchronously when the event is fired. 49 if (syncCallback) { 50 syncCallback(subject); 51 } 52 resolve(subject); 53 } 54 }, 55 }; 56 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 57 const id = setTimeout(timeout, 90000); 58 addObserver(observer, topic, false); 59 queue.push(topic, resolve); 60 }); 61 }; 62 receive.queue = []; 63 64 const openTab = uri => 65 (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, uri)); 66 67 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 68 const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); 69 70 const isData = document => document.URL.startsWith("data:"); 71 72 const uri1 = "data:text/html;charset=utf-8,<h1>1</h1>"; 73 // For whatever reason going back on load event doesn't work so timeout it is :( 74 const uri2 = 75 "data:text/html;charset=utf-8,<h1>2</h1><script>setTimeout(SpecialPowers.wrap(window).back,100)</script>"; 76 const uri3 = "data:text/html;charset=utf-8,<h1>3</h1>"; 77 78 const uri4 = "chrome://browser/content/license.html"; 79 80 const test = Test(async function () { 81 let documentInteractive = receive( 82 "content-document-interactive", 83 isData, 84 d => { 85 // This test is executed synchronously when the event is received. 86 is(d.readyState, "interactive", "document is interactive"); 87 is(d.URL, uri1, "document.URL matches tab url"); 88 } 89 ); 90 let documentLoaded = receive("content-document-loaded", isData); 91 let pageShown = receive("content-page-shown", isData); 92 93 info("open: uri#1"); 94 const tab1 = openTab(uri1); 95 const browser1 = gBrowser.getBrowserForTab(tab1); 96 97 let interactiveDocument1 = await documentInteractive; 98 99 let loadedDocument1 = await documentLoaded; 100 is(loadedDocument1.readyState, "complete", "document is loaded"); 101 is(interactiveDocument1, loadedDocument1, "interactive document is loaded"); 102 103 let shownPage = await pageShown; 104 is(interactiveDocument1, shownPage, "loaded document is shown"); 105 106 // Wait until history entry is created before loading new uri. 107 await receive("sessionstore-state-write-complete"); 108 109 info("load uri#2"); 110 111 documentInteractive = receive("content-document-interactive", isData, d => { 112 // This test is executed synchronously when the event is received. 113 is(d.readyState, "interactive", "document is interactive"); 114 is(d.URL, uri2, "document.URL matches URL loaded"); 115 }); 116 documentLoaded = receive("content-document-loaded", isData); 117 pageShown = receive("content-page-shown", isData); 118 let pageHidden = receive("content-page-hidden", isData); 119 120 BrowserTestUtils.startLoadingURIString(browser1, uri2); 121 122 let hiddenPage = await pageHidden; 123 is(interactiveDocument1, hiddenPage, "loaded document is hidden"); 124 125 let interactiveDocument2 = await documentInteractive; 126 127 let loadedDocument2 = await documentLoaded; 128 is(loadedDocument2.readyState, "complete", "document is loaded"); 129 is(interactiveDocument2, loadedDocument2, "interactive document is loaded"); 130 131 shownPage = await pageShown; 132 is(interactiveDocument2, shownPage, "loaded document is shown"); 133 134 info("go back to uri#1"); 135 136 documentInteractive = receive("content-document-interactive", isData, d => { 137 // This test is executed synchronously when the event is received. 138 is(d.readyState, "interactive", "document is interactive"); 139 is(d.URL, uri3, "document.URL matches URL loaded"); 140 }); 141 documentLoaded = receive("content-document-loaded", isData); 142 pageShown = receive("content-page-shown", isData); 143 pageHidden = receive("content-page-hidden", isData); 144 145 hiddenPage = await pageHidden; 146 is(interactiveDocument2, hiddenPage, "new document is hidden"); 147 148 shownPage = await pageShown; 149 is(interactiveDocument1, shownPage, "previous document is shown"); 150 151 info("load uri#3"); 152 153 BrowserTestUtils.startLoadingURIString(browser1, uri3); 154 155 pageShown = receive("content-page-shown", isData); 156 157 let interactiveDocument3 = await documentInteractive; 158 159 let loadedDocument3 = await documentLoaded; 160 is(loadedDocument3.readyState, "complete", "document is loaded"); 161 is(interactiveDocument3, loadedDocument3, "interactive document is loaded"); 162 163 shownPage = await pageShown; 164 is(interactiveDocument3, shownPage, "previous document is shown"); 165 166 gBrowser.removeTab(tab1); 167 168 info("load chrome uri"); 169 170 const tab2 = openTab(uri4); 171 documentInteractive = receive("chrome-document-interactive", null, d => { 172 // This test is executed synchronously when the event is received. 173 is(d.readyState, "interactive", "document is interactive"); 174 is(d.URL, uri4, "document.URL matches URL loaded"); 175 }); 176 documentLoaded = receive("chrome-document-loaded"); 177 pageShown = receive("chrome-page-shown"); 178 179 const interactiveDocument4 = await documentInteractive; 180 181 let loadedDocument4 = await documentLoaded; 182 is(loadedDocument4.readyState, "complete", "document is loaded"); 183 is(interactiveDocument4, loadedDocument4, "interactive document is loaded"); 184 185 shownPage = await pageShown; 186 is(interactiveDocument4, shownPage, "loaded chrome document is shown"); 187 188 pageHidden = receive("chrome-page-hidden"); 189 gBrowser.removeTab(tab2); 190 191 hiddenPage = await pageHidden; 192 is(interactiveDocument4, hiddenPage, "chrome document hidden"); 193 });