browser_formdata.js (6925B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 requestLongerTimeout(2); 7 8 /** 9 * This test ensures that form data collection respects the privacy level as 10 * set by the user. 11 */ 12 add_task(async function test_formdata() { 13 const URL = 14 "http://mochi.test:8888/browser/browser/components/" + 15 "sessionstore/test/browser_formdata_sample.html"; 16 17 const OUTER_VALUE = "browser_formdata_" + Math.random(); 18 const INNER_VALUE = "browser_formdata_" + Math.random(); 19 20 // Creates a tab, loads a page with some form fields, 21 // modifies their values and closes the tab. 22 async function createAndRemoveTab() { 23 // Create a new tab. 24 let tab = BrowserTestUtils.addTab(gBrowser, URL); 25 let browser = tab.linkedBrowser; 26 await promiseBrowserLoaded(browser); 27 28 // Modify form data. 29 await setPropertyOfFormField(browser, "#txt", "value", OUTER_VALUE); 30 await setPropertyOfFormField( 31 browser.browsingContext.children[0], 32 "#txt", 33 "value", 34 INNER_VALUE 35 ); 36 37 // Remove the tab. 38 await promiseRemoveTabAndSessionState(tab); 39 } 40 41 await createAndRemoveTab(); 42 let [ 43 { 44 state: { formdata }, 45 }, 46 ] = ss.getClosedTabDataForWindow(window); 47 is(formdata.id.txt, OUTER_VALUE, "outer value is correct"); 48 is(formdata.children[0].id.txt, INNER_VALUE, "inner value is correct"); 49 50 // Disable saving data for encrypted sites. 51 Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1); 52 53 await createAndRemoveTab(); 54 [ 55 { 56 state: { formdata }, 57 }, 58 ] = ss.getClosedTabDataForWindow(window); 59 is(formdata.id.txt, OUTER_VALUE, "outer value is correct"); 60 ok(!formdata.children, "inner value was *not* stored"); 61 62 // Disable saving data for any site. 63 Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2); 64 65 await createAndRemoveTab(); 66 [ 67 { 68 state: { formdata }, 69 }, 70 ] = ss.getClosedTabDataForWindow(window); 71 ok(!formdata, "form data has *not* been stored"); 72 73 // Restore the default privacy level. 74 Services.prefs.clearUserPref("browser.sessionstore.privacy_level"); 75 }); 76 77 /** 78 * This test ensures that a malicious website can't trick us into restoring 79 * form data into a wrong website and that we always check the stored URL 80 * before doing so. 81 */ 82 add_task(async function test_url_check() { 83 const URL = "data:text/html;charset=utf-8,<input id=input>"; 84 const VALUE = "value-" + Math.random(); 85 86 // Create a tab with an iframe containing an input field. 87 let tab = BrowserTestUtils.addTab(gBrowser, URL); 88 let browser = tab.linkedBrowser; 89 await promiseBrowserLoaded(browser); 90 91 // Restore a tab state with a given form data url. 92 async function restoreStateWithURL(url) { 93 let state = { 94 entries: [{ url: URL, triggeringPrincipal_base64 }], 95 formdata: { id: { input: VALUE } }, 96 }; 97 98 if (url) { 99 state.formdata.url = url; 100 } 101 102 await promiseTabState(tab, state); 103 return getPropertyOfFormField(browser, "#input", "value"); 104 } 105 106 // Check that the form value is restored with the correct URL. 107 is(await restoreStateWithURL(URL), VALUE, "form data restored"); 108 109 // Check that the form value is *not* restored with the wrong URL. 110 is(await restoreStateWithURL(URL + "?"), "", "form data not restored"); 111 is(await restoreStateWithURL(), "", "form data not restored"); 112 113 // Cleanup. 114 gBrowser.removeTab(tab); 115 }); 116 117 /** 118 * This test ensures that collecting form data works as expected when having 119 * nested frame sets. 120 */ 121 add_task(async function test_nested() { 122 const URL = 123 "data:text/html;charset=utf-8," + 124 "<iframe src='data:text/html;charset=utf-8,<input/>'/>"; 125 126 const FORM_DATA = { 127 children: [ 128 { 129 url: "data:text/html;charset=utf-8,<input/>", 130 xpath: { "/xhtml:html/xhtml:body/xhtml:input": "m" }, 131 }, 132 ], 133 }; 134 135 // Create a tab with an iframe containing an input field. 136 let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, URL)); 137 let browser = tab.linkedBrowser; 138 await promiseBrowserLoaded(browser, false /* don't ignore subframes */); 139 140 const iframe = await SpecialPowers.spawn(browser, [], () => { 141 return content.document.querySelector("iframe").browsingContext; 142 }); 143 await SpecialPowers.spawn(iframe, [], async () => { 144 const input = content.document.querySelector("input"); 145 const focusPromise = new Promise(resolve => { 146 input.addEventListener("focus", resolve, { once: true }); 147 }); 148 input.focus(); 149 await focusPromise; 150 }); 151 152 // Modify the input field's value. 153 await BrowserTestUtils.synthesizeKey("m", {}, browser); 154 155 // Remove the tab and check that we stored form data correctly. 156 await promiseRemoveTabAndSessionState(tab); 157 let [ 158 { 159 state: { formdata }, 160 }, 161 ] = ss.getClosedTabDataForWindow(window); 162 is( 163 JSON.stringify(formdata), 164 JSON.stringify(FORM_DATA), 165 "formdata for iframe stored correctly" 166 ); 167 168 // Restore the closed tab. 169 tab = ss.undoCloseTab(window, 0); 170 browser = tab.linkedBrowser; 171 await promiseTabRestored(tab); 172 173 // Check that the input field has the right value. 174 await TabStateFlusher.flush(browser); 175 ({ formdata } = JSON.parse(ss.getTabState(tab))); 176 is( 177 JSON.stringify(formdata), 178 JSON.stringify(FORM_DATA), 179 "formdata for iframe restored correctly" 180 ); 181 182 // Cleanup. 183 gBrowser.removeTab(tab); 184 }); 185 186 /** 187 * This test ensures that collecting form data for documents with 188 * designMode=on works as expected. 189 */ 190 add_task(async function test_design_mode() { 191 const URL = 192 "data:text/html;charset=utf-8,<h1>mozilla</h1>" + 193 "<script>document.designMode='on'</script>"; 194 195 // Load a tab with an editable document. 196 let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, URL)); 197 let browser = tab.linkedBrowser; 198 await promiseBrowserLoaded(browser); 199 200 // Modify the document content. 201 await BrowserTestUtils.synthesizeKey("m", {}, browser); 202 203 // Close and restore the tab. 204 await promiseRemoveTabAndSessionState(tab); 205 tab = ss.undoCloseTab(window, 0); 206 browser = tab.linkedBrowser; 207 await promiseTabRestored(tab); 208 209 // Check that the innerHTML value was restored. 210 let html = await getPropertyOfFormField(browser, "body", "innerHTML"); 211 let expected = "<h1>mmozilla</h1><script>document.designMode='on'</script>"; 212 is(html, expected, "editable document has been restored correctly"); 213 214 // Close and restore the tab. 215 await promiseRemoveTabAndSessionState(tab); 216 tab = ss.undoCloseTab(window, 0); 217 browser = tab.linkedBrowser; 218 await promiseTabRestored(tab); 219 220 // Check that the innerHTML value was restored. 221 html = await getPropertyOfFormField(browser, "body", "innerHTML"); 222 expected = "<h1>mmozilla</h1><script>document.designMode='on'</script>"; 223 is(html, expected, "editable document has been restored correctly"); 224 225 // Cleanup. 226 gBrowser.removeTab(tab); 227 });