test_unicodeCharacters.js (5706B)
1 /** 2 * Any copyright is dedicated to the Public Domain. 3 * http://creativecommons.org/publicdomain/zero/1.0/ 4 */ 5 6 const interpretChar = (chars, index) => { 7 return chars.charCodeAt(index).toString(16).padStart(4, "0"); 8 }; 9 10 const hexEncode = str => { 11 let result = ""; 12 const len = str.length; 13 for (let i = 0; i < len; ++i) { 14 result += interpretChar(str, i); 15 } 16 return result; 17 }; 18 19 const collectCorrupted = (expected, actual) => { 20 const len = Math.min(expected.length, actual.length); 21 let notEquals = []; 22 for (let i = 0; i < len; ++i) { 23 if (expected[i] !== actual[i]) { 24 notEquals.push([hexEncode(expected[i]), hexEncode(actual[i])]); 25 } 26 } 27 return notEquals; 28 }; 29 30 const sanitizeOutputWithSurrogates = (testValue, prefix = "") => { 31 let utf8What = prefix; 32 for (let i = 0; i < testValue.length; ++i) { 33 const valueChar = testValue.charCodeAt(i); 34 const isPlanar = 0xd800 <= valueChar && valueChar <= 0xdfff; 35 utf8What += isPlanar ? "\\u" + interpretChar(testValue, i) : testValue[i]; 36 } 37 return utf8What; 38 }; 39 40 const getEncodingSample = () => { 41 const expectedSample = 42 "3681207208613504e0a5028800b945551988c60050008027ebc2808c00d38e806e03d8210ac906722b85499be9d00000"; 43 44 let result = ""; 45 const len = expectedSample.length; 46 for (let i = 0; i < len; i += 4) { 47 result += String.fromCharCode(parseInt(expectedSample.slice(i, i + 4), 16)); 48 } 49 return result; 50 }; 51 52 const getSeparatedBasePlane = () => { 53 let result = ""; 54 for (let i = 0xffff; i >= 0; --i) { 55 result += String.fromCharCode(i) + "\n"; 56 } 57 return result; 58 }; 59 60 const getJoinedBasePlane = () => { 61 let result = ""; 62 for (let i = 0; i <= 0xffff; ++i) { 63 result += String.fromCharCode(i); 64 } 65 return result; 66 }; 67 68 const getSurrogateCombinations = () => { 69 const upperLead = String.fromCharCode(0xdbff); 70 const lowerTrail = String.fromCharCode(0xdc00); 71 72 const regularSlot = ["w", "abcdefghijklmnopqrst", "aaaaaaaaaaaaaaaaaaaa", ""]; 73 const surrogateSlot = [lowerTrail, upperLead]; 74 75 let samples = []; 76 for (const leadSnippet of regularSlot) { 77 for (const firstSlot of surrogateSlot) { 78 for (const trailSnippet of regularSlot) { 79 for (const secondSlot of surrogateSlot) { 80 samples.push(leadSnippet + firstSlot + secondSlot + trailSnippet); 81 } 82 samples.push(leadSnippet + firstSlot + trailSnippet); 83 } 84 } 85 } 86 87 return samples; 88 }; 89 90 const fetchFrom = async (itemKey, sample, meanwhile) => { 91 const principal = getPrincipal("http://example.com/", {}); 92 93 let request = clearOrigin(principal); 94 await requestFinished(request); 95 96 const storage = getLocalStorage(principal); 97 98 await storage.setItem(itemKey, sample); 99 100 await meanwhile(principal); 101 102 return storage.getItem(itemKey); 103 }; 104 105 /** 106 * Value fetched from existing snapshot based on 107 * existing in-memory datastore in the parent process 108 * without any communication between content/parent 109 */ 110 const fetchFromExistingSnapshotExistingDatastore = async (itemKey, sample) => { 111 return fetchFrom(itemKey, sample, async () => {}); 112 }; 113 114 /** 115 * Value fetched from newly created snapshot based on 116 * existing in-memory datastore in the parent process 117 */ 118 const fetchFromNewSnapshotExistingDatastore = async (itemKey, sample) => { 119 return fetchFrom(itemKey, sample, async () => { 120 await returnToEventLoop(); 121 }); 122 }; 123 124 /** 125 * Value fetched from newly created snapshot based on newly created 126 * in-memory datastore based on database in the parent process 127 */ 128 const fetchFromNewSnapshotNewDatastore = async (itemKey, sample) => { 129 return fetchFrom(itemKey, sample, async principal => { 130 let request = resetClient(principal); 131 await requestFinished(request); 132 }); 133 }; 134 135 add_task(async function testSteps() { 136 /* This test is based on bug 1681300 */ 137 Services.prefs.setBoolPref( 138 "dom.storage.enable_unsupported_legacy_implementation", 139 false 140 ); 141 Services.prefs.setBoolPref("dom.storage.snapshot_reusing", false); 142 143 const reportWhat = (testKey, testValue) => { 144 if (testKey.length + testValue.length > 82) { 145 return testKey; 146 } 147 return sanitizeOutputWithSurrogates(testValue, /* prefix */ testKey + ":"); 148 }; 149 150 const testFetchMode = async (testType, storeAndLookup) => { 151 const testPairs = [ 152 { testEmptyValue: [""] }, 153 { testSampleKey: [getEncodingSample()] }, 154 { testSeparatedKey: [getSeparatedBasePlane()] }, 155 { testJoinedKey: [getJoinedBasePlane()] }, 156 { testCombinations: getSurrogateCombinations() }, 157 ]; 158 159 for (const testPair of testPairs) { 160 for (const [testKey, expectedValues] of Object.entries(testPair)) { 161 for (const expected of expectedValues) { 162 const actual = await storeAndLookup(testKey, expected); 163 const testInfo = reportWhat(testKey, expected); 164 is( 165 null != actual, 166 true, 167 testType + ": Value not null for " + testInfo 168 ); 169 is( 170 expected.length, 171 actual.length, 172 testType + ": Returned size for " + testInfo 173 ); 174 175 const notEquals = collectCorrupted(expected, actual); 176 for (let i = 0; i < notEquals.length; ++i) { 177 is( 178 notEquals[i][0], 179 notEquals[i][1], 180 testType + ": Unequal character at " + i + " for " + testInfo 181 ); 182 } 183 } 184 } 185 } 186 }; 187 188 await testFetchMode( 189 "ExistingSnapshotExistingDatastore", 190 fetchFromExistingSnapshotExistingDatastore 191 ); 192 193 await testFetchMode( 194 "NewSnapshotExistingDatastore", 195 fetchFromNewSnapshotExistingDatastore 196 ); 197 198 await testFetchMode( 199 "NewSnapshotNewDatastore", 200 fetchFromNewSnapshotNewDatastore 201 ); 202 });