base64.any.js (5267B)
1 // META: global=window,dedicatedworker,shadowrealm 2 3 /** 4 * btoa() as defined by the HTML5 spec, which mostly just references RFC4648. 5 */ 6 function mybtoa(s) { 7 // String conversion as required by WebIDL. 8 s = String(s); 9 10 // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the 11 // method's first argument contains any character whose code point is 12 // greater than U+00FF." 13 for (var i = 0; i < s.length; i++) { 14 if (s.charCodeAt(i) > 255) { 15 return "INVALID_CHARACTER_ERR"; 16 } 17 } 18 19 var out = ""; 20 for (var i = 0; i < s.length; i += 3) { 21 var groupsOfSix = [undefined, undefined, undefined, undefined]; 22 groupsOfSix[0] = s.charCodeAt(i) >> 2; 23 groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; 24 if (s.length > i + 1) { 25 groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; 26 groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; 27 } 28 if (s.length > i + 2) { 29 groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; 30 groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; 31 } 32 for (var j = 0; j < groupsOfSix.length; j++) { 33 if (typeof groupsOfSix[j] == "undefined") { 34 out += "="; 35 } else { 36 out += btoaLookup(groupsOfSix[j]); 37 } 38 } 39 } 40 return out; 41 } 42 43 /** 44 * Lookup table for mybtoa(), which converts a six-bit number into the 45 * corresponding ASCII character. 46 */ 47 function btoaLookup(idx) { 48 if (idx < 26) { 49 return String.fromCharCode(idx + 'A'.charCodeAt(0)); 50 } 51 if (idx < 52) { 52 return String.fromCharCode(idx - 26 + 'a'.charCodeAt(0)); 53 } 54 if (idx < 62) { 55 return String.fromCharCode(idx - 52 + '0'.charCodeAt(0)); 56 } 57 if (idx == 62) { 58 return '+'; 59 } 60 if (idx == 63) { 61 return '/'; 62 } 63 // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests. 64 } 65 66 function btoaException(input) { 67 input = String(input); 68 for (var i = 0; i < input.length; i++) { 69 if (input.charCodeAt(i) > 255) { 70 return true; 71 } 72 } 73 return false; 74 } 75 76 function testBtoa(input) { 77 // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the 78 // method's first argument contains any character whose code point is 79 // greater than U+00FF." 80 var normalizedInput = String(input); 81 for (var i = 0; i < normalizedInput.length; i++) { 82 if (normalizedInput.charCodeAt(i) > 255) { 83 assert_throws_dom("InvalidCharacterError", function() { btoa(input); }, 84 "Code unit " + i + " has value " + normalizedInput.charCodeAt(i) + ", which is greater than 255"); 85 return; 86 } 87 } 88 assert_equals(btoa(input), mybtoa(input)); 89 assert_equals(atob(btoa(input)), String(input), "atob(btoa(input)) must be the same as String(input)"); 90 } 91 92 var tests = ["עברית", "", "ab", "abc", "abcd", "abcde", 93 // This one is thrown in because IE9 seems to fail atob(btoa()) on it. Or 94 // possibly to fail btoa(). I actually can't tell what's happening here, 95 // but it doesn't hurt. 96 "\xff\xff\xc0", 97 // Is your DOM implementation binary-safe? 98 "\0a", "a\0b", 99 // WebIDL tests. 100 undefined, null, 7, 12, 1.5, true, false, NaN, +Infinity, -Infinity, 0, -0, 101 {toString: function() { return "foo" }}, 102 ]; 103 for (var i = 0; i < 258; i++) { 104 tests.push(String.fromCharCode(i)); 105 } 106 tests.push(String.fromCharCode(10000)); 107 tests.push(String.fromCharCode(65534)); 108 tests.push(String.fromCharCode(65535)); 109 110 // This is supposed to be U+10000. 111 tests.push(String.fromCharCode(0xd800, 0xdc00)); 112 tests = tests.map( 113 function(elem) { 114 var expected = mybtoa(elem); 115 if (expected === "INVALID_CHARACTER_ERR") { 116 return ["btoa(" + format_value(elem) + ") must raise INVALID_CHARACTER_ERR", elem]; 117 } 118 return ["btoa(" + format_value(elem) + ") == " + format_value(mybtoa(elem)), elem]; 119 } 120 ); 121 122 var everything = ""; 123 for (var i = 0; i < 256; i++) { 124 everything += String.fromCharCode(i); 125 } 126 tests.push(["btoa(first 256 code points concatenated)", everything]); 127 128 generate_tests(testBtoa, tests); 129 130 promise_test(() => fetch_json("../../../fetch/data-urls/resources/base64.json").then(runAtobTests), "atob() setup."); 131 132 const idlTests = [ 133 [undefined, null], 134 [null, [158, 233, 101]], 135 [7, null], 136 [12, [215]], 137 [1.5, null], 138 [true, [182, 187]], 139 [false, null], 140 [NaN, [53, 163]], 141 [+Infinity, [34, 119, 226, 158, 43, 114]], 142 [-Infinity, null], 143 [0, null], 144 [-0, null], 145 [{toString: function() { return "foo" }}, [126, 138]], 146 [{toString: function() { return "abcd" }}, [105, 183, 29]] 147 ]; 148 149 function runAtobTests(tests) { 150 const allTests = tests.concat(idlTests); 151 for(let i = 0; i < allTests.length; i++) { 152 const input = allTests[i][0], 153 output = allTests[i][1]; 154 test(() => { 155 if(output === null) { 156 assert_throws_dom("InvalidCharacterError", () => globalThis.atob(input)); 157 } else { 158 const result = globalThis.atob(input); 159 for(let ii = 0; ii < output.length; ii++) { 160 assert_equals(result.charCodeAt(ii), output[ii]); 161 } 162 } 163 }, "atob(" + format_value(input) + ")"); 164 } 165 }