test_ext_tabs_executeScript_runAt.html (4313B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Tabs executeScript runAt Test</title> 6 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> 7 <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script> 8 <script type="text/javascript" src="head.js"></script> 9 <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> 10 </head> 11 <body> 12 13 <script type="text/javascript"> 14 "use strict"; 15 16 /** 17 * These tests ensure that the runAt argument to tabs.executeScript delays 18 * script execution until the document has reached the correct state. 19 * 20 * Since tests of this nature are especially race-prone, it relies on a 21 * server-JS script to delay the completion of our test page's load cycle long 22 * enough for us to attempt to load our scripts in the earlies phase we support. 23 * 24 * And since we can't actually rely on that timing, it retries any attempts that 25 * fail to load as early as expected, but don't load at any illegal time. 26 */ 27 28 add_task(async function testExecuteScript() { 29 const win = window.open("about:blank"); 30 31 async function background(DEBUG) { 32 let tab; 33 34 const BASE = "http://mochi.test:8888/tests/mobile/shared/components/extensions/test/mochitest/"; 35 const URL = BASE + "file_slowed_document.sjs"; 36 37 const MAX_TRIES = 30; 38 39 const onUpdatedPromise = (tabId, url, status) => { 40 return new Promise(resolve => { 41 browser.tabs.onUpdated.addListener(function listener(_, changed, tab) { 42 if (tabId == tab.id && changed.status == status && tab.url == url) { 43 browser.tabs.onUpdated.removeListener(listener); 44 resolve(); 45 } 46 }); 47 }); 48 }; 49 50 try { 51 [tab] = await browser.tabs.query({active: true, currentWindow: true}); 52 53 let success = false; 54 for (let tries = 0; !success && tries < MAX_TRIES; tries++) { 55 const url = `${URL}?with-iframe&r=${Math.random()}`; 56 57 const loadingPromise = onUpdatedPromise(tab.id, url, "loading"); 58 const completePromise = onUpdatedPromise(tab.id, url, "complete"); 59 60 // TODO: Test allFrames and frameId. 61 62 await browser.tabs.update({url}); 63 await loadingPromise; 64 65 const states = await Promise.all([ 66 // Send the executeScript requests in the reverse order that we expect 67 // them to execute in, to avoid them passing only because of timing 68 // races. 69 browser.tabs.executeScript({ 70 code: "document.readyState", 71 runAt: "document_idle", 72 }), 73 browser.tabs.executeScript({ 74 code: "document.readyState", 75 runAt: "document_end", 76 }), 77 browser.tabs.executeScript({ 78 code: "document.readyState", 79 runAt: "document_start", 80 }), 81 ].reverse()); 82 83 browser.test.log(`Got states: ${states}`); 84 85 // Make sure that none of our scripts executed earlier than expected, 86 // regardless of retries. 87 browser.test.assertTrue(states[1] == "interactive" || states[1] == "complete", 88 `document_end state is valid: ${states[1]}`); 89 browser.test.assertTrue(states[2] == "interactive" || states[2] == "complete", 90 `document_idle state is valid: ${states[2]}`); 91 92 // If we have the earliest valid states for each script, we're done. 93 // Otherwise, try again. 94 success = ((states[0] == "loading" || DEBUG) && 95 states[1] == "interactive" && 96 (states[2] == "interactive" || states[2] == "complete")); 97 98 await completePromise; 99 } 100 101 browser.test.assertTrue(success, "Got the earliest expected states at least once"); 102 103 browser.test.notifyPass("executeScript-runAt"); 104 } catch (e) { 105 browser.test.fail(`Error: ${e} :: ${e.stack}`); 106 browser.test.notifyFail("executeScript-runAt"); 107 } 108 } 109 110 const extension = ExtensionTestUtils.loadExtension({ 111 manifest: { 112 "permissions": ["http://mochi.test/", "tabs"], 113 }, 114 background: `(${background})(${AppConstants.DEBUG})`, 115 }); 116 117 await extension.startup(); 118 119 await extension.awaitFinish("executeScript-runAt"); 120 121 await extension.unload(); 122 123 win.close(); 124 }); 125 </script> 126 127 </body> 128 </html>