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