browser_formdata_xpath.js (6685B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const URL = ROOT + "browser_formdata_xpath_sample.html"; 7 8 /** 9 * Bug 346337 - Generic form data restoration tests. 10 */ 11 add_setup(function () { 12 // make sure we don't save form data at all (except for tab duplication) 13 Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2); 14 15 registerCleanupFunction(() => { 16 Services.prefs.clearUserPref("browser.sessionstore.privacy_level"); 17 }); 18 }); 19 20 const FILE1 = createFilePath("346337_test1.file"); 21 const FILE2 = createFilePath("346337_test2.file"); 22 23 const FIELDS = { 24 "//input[@name='input']": Date.now().toString(16), 25 "//input[@name='spaced 1']": Math.random().toString(), 26 "//input[3]": "three", 27 "//input[@type='checkbox']": true, 28 "//input[@name='uncheck']": false, 29 "//input[@type='radio'][1]": false, 30 "//input[@type='radio'][2]": true, 31 "//input[@type='radio'][3]": false, 32 "//select": 2, 33 "//select[@multiple]": [1, 3], 34 "//textarea[1]": "", 35 "//textarea[2]": "Some text... " + Math.random(), 36 "//textarea[3]": "Some more text\n" + new Date(), 37 "//input[@type='file'][1]": [FILE1], 38 "//input[@type='file'][2]": [FILE1, FILE2], 39 }; 40 41 add_task(async function test_form_data_restoration() { 42 // Load page with some input fields. 43 let tab = BrowserTestUtils.addTab(gBrowser, URL); 44 let browser = tab.linkedBrowser; 45 await promiseBrowserLoaded(browser); 46 47 // Fill in some values. 48 for (let xpath of Object.keys(FIELDS)) { 49 await setFormValue(browser, xpath); 50 } 51 52 // Duplicate the tab. 53 let tab2 = gBrowser.duplicateTab(tab); 54 let browser2 = tab2.linkedBrowser; 55 await promiseTabRestored(tab2); 56 57 // Check that all form values have been duplicated. 58 for (let xpath of Object.keys(FIELDS)) { 59 let expected = JSON.stringify(FIELDS[xpath]); 60 let actual = JSON.stringify(await getFormValue(browser2, xpath)); 61 is( 62 actual, 63 expected, 64 'The value for "' + xpath + '" was correctly restored' 65 ); 66 } 67 68 // Remove all tabs. 69 await promiseRemoveTabAndSessionState(tab2); 70 await promiseRemoveTabAndSessionState(tab); 71 72 // Restore one of the tabs again. 73 tab = ss.undoCloseTab(window, 0); 74 browser = tab.linkedBrowser; 75 await promiseTabRestored(tab); 76 77 // Check that none of the form values have been restored due to the privacy 78 // level settings. 79 for (let xpath of Object.keys(FIELDS)) { 80 let expected = FIELDS[xpath]; 81 if (expected) { 82 let actual = await getFormValue(browser, xpath, expected); 83 isnot( 84 actual, 85 expected, 86 'The value for "' + xpath + '" was correctly discarded' 87 ); 88 } 89 } 90 91 // Cleanup. 92 BrowserTestUtils.removeTab(tab); 93 }); 94 95 function getPropertyOfXPath(browserContext, path, propName) { 96 return SpecialPowers.spawn( 97 browserContext, 98 [path, propName], 99 (pathChild, propNameChild) => { 100 let doc = content.document; 101 let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE; 102 let node = doc.evaluate( 103 pathChild, 104 doc, 105 null, 106 xptype, 107 null 108 ).singleNodeValue; 109 return node[propNameChild]; 110 } 111 ); 112 } 113 114 function setPropertyOfXPath(browserContext, path, propName, newValue) { 115 return SpecialPowers.spawn( 116 browserContext, 117 [path, propName, newValue], 118 (pathChild, propNameChild, newValueChild) => { 119 let doc = content.document; 120 let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE; 121 let node = doc.evaluate( 122 pathChild, 123 doc, 124 null, 125 xptype, 126 null 127 ).singleNodeValue; 128 node[propNameChild] = newValueChild; 129 130 let event = node.ownerDocument.createEvent("UIEvents"); 131 event.initUIEvent("input", true, true, node.ownerGlobal, 0); 132 node.dispatchEvent(event); 133 } 134 ); 135 } 136 137 function execUsingXPath(browserContext, path, fnName, arg) { 138 return SpecialPowers.spawn( 139 browserContext, 140 [path, fnName, arg], 141 (pathChild, fnNameChild, argChild) => { 142 let doc = content.document; 143 let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE; 144 let node = doc.evaluate( 145 pathChild, 146 doc, 147 null, 148 xptype, 149 null 150 ).singleNodeValue; 151 152 switch (fnNameChild) { 153 case "getMultipleSelected": 154 return Array.from(node.options, (opt, idx) => idx).filter( 155 idx => node.options[idx].selected 156 ); 157 case "setMultipleSelected": 158 Array.prototype.forEach.call( 159 node.options, 160 (opt, idx) => (opt.selected = argChild.indexOf(idx) > -1) 161 ); 162 break; 163 case "getFileNameArray": 164 return node.mozGetFileNameArray(); 165 case "setFileNameArray": 166 node.mozSetFileNameArray(argChild, argChild.length); 167 break; 168 } 169 170 let event = node.ownerDocument.createEvent("UIEvents"); 171 event.initUIEvent("input", true, true, node.ownerGlobal, 0); 172 node.dispatchEvent(event); 173 return undefined; 174 } 175 ); 176 } 177 178 function createFilePath(leaf) { 179 let file = Services.dirsvc.get("TmpD", Ci.nsIFile); 180 file.append(leaf); 181 return file.path; 182 } 183 184 function isArrayOfNumbers(value) { 185 return Array.isArray(value) && value.every(n => typeof n === "number"); 186 } 187 188 function isArrayOfStrings(value) { 189 return Array.isArray(value) && value.every(n => typeof n === "string"); 190 } 191 192 function getFormValue(browser, xpath) { 193 let value = FIELDS[xpath]; 194 195 if (typeof value == "string") { 196 return getPropertyOfXPath(browser, xpath, "value"); 197 } 198 199 if (typeof value == "boolean") { 200 return getPropertyOfXPath(browser, xpath, "checked"); 201 } 202 203 if (typeof value == "number") { 204 return getPropertyOfXPath(browser, xpath, "selectedIndex"); 205 } 206 207 if (isArrayOfNumbers(value)) { 208 return execUsingXPath(browser, xpath, "getMultipleSelected"); 209 } 210 211 if (isArrayOfStrings(value)) { 212 return execUsingXPath(browser, xpath, "getFileNameArray"); 213 } 214 215 throw new Error("unknown input type"); 216 } 217 218 function setFormValue(browser, xpath) { 219 let value = FIELDS[xpath]; 220 221 if (typeof value == "string") { 222 return setPropertyOfXPath(browser, xpath, "value", value); 223 } 224 225 if (typeof value == "boolean") { 226 return setPropertyOfXPath(browser, xpath, "checked", value); 227 } 228 229 if (typeof value == "number") { 230 return setPropertyOfXPath(browser, xpath, "selectedIndex", value); 231 } 232 233 if (isArrayOfNumbers(value)) { 234 return execUsingXPath(browser, xpath, "setMultipleSelected", value); 235 } 236 237 if (isArrayOfStrings(value)) { 238 return execUsingXPath(browser, xpath, "setFileNameArray", value); 239 } 240 241 throw new Error("unknown input type"); 242 }