LCOV - code coverage report
Current view: top level - lib/helper - whatwg-misc.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 617 717 86.1 %
Date: 2024-12-07 00:20:21 Functions: 14 16 87.5 %
Branches: 41 54 75.9 %

           Branch data     Line data    Source code
       1            [ + ]:        348 : /**
       2                 :        348 :  * @module  whatwg-misc
       3                 :        348 :  * @desc    Helper module - Helper functions for the main module {@link module:whatwg-xhr whatwg-xhr}.
       4                 :        348 :  * @version 1.0.0
       5                 :        348 :  * @author  Essam A. El-Sherif
       6                 :        348 :  */
       7                 :        348 : 
       8                 :        348 : /* Import nodeJS core modules */
       9                 :        348 : import assert   from 'node:assert/strict';
      10                 :        348 : import { atob } from 'node:buffer';
      11                 :        348 : 
      12                 :        348 : /*
      13                 :        348 :  * HTTP token code point
      14                 :        348 :  * url: https://mimesniff.spec.whatwg.org/#http-token-code-point
      15                 :        348 :  *
      16                 :        348 :  * @const HTTP_TOKEN_CODEPOINTS
      17                 :        348 :  * @desc  An HTTP token code point, as defined by {@link https://mimesniff.spec.whatwg.org/#http-token-code-point WHATWG MIME Sniffing Living Standard}.
      18                 :        348 :  */
      19                 :        348 : const HTTP_TOKEN_CODEPOINTS = /^[!#$%&'*+-.^_|~A-z0-9]+$/;
      20                 :        348 : 
      21                 :        348 : /*
      22                 :        348 :  * A whitespace byte (abbreviated 0xWS)
      23                 :        348 :  * url: https://mimesniff.spec.whatwg.org/#whitespace-byte
      24                 :        348 :  *
      25                 :        348 :  * @const HTTP_WHITESPACE_REGEX
      26                 :        348 :  * @desc  A whitespace byte, as defined by {@link https://mimesniff.spec.whatwg.org/#whitespace-byte WHATWG MIME Sniffing Living Standard}.
      27                 :        348 :  */
      28                 :        348 : const HTTP_WHITESPACE_REGEX = /(\u000A|\u000D|\u0009|\u0020)/;
      29                 :        348 : 
      30                 :        348 : /*
      31                 :        348 :  * HTTP quoted-string token code point
      32                 :        348 :  * url: https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
      33                 :        348 :  *
      34                 :        348 :  * @const HTTP_QUOTED_STRING_TOKENS
      35                 :        348 :  * @desc  HTTP quoted-string token code point, as defined by {@link https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point WHATWG MIME Sniffing Living Standard}.
      36                 :        348 :  */
      37                 :        348 : const HTTP_QUOTED_STRING_TOKENS = /^(\u0009|\x{0020}-\x{007E}|\x{0080}-\x{00FF})+$/;
      38                 :        348 : 
      39                 :        348 : /**
      40                 :        348 :  * The data: URL processor takes a URL dataURL and then runs these steps:
      41                 :        348 :  * url: https://fetch.spec.whatwg.org/#data-url-processor
      42                 :        348 :  *
      43                 :        348 :  *  1. Assert: dataURL’s scheme is "data".
      44                 :        348 :  *  2. Let input be the result of running the URL serializer on dataURL with exclude fragment set to true.
      45                 :        348 :  *  3. Remove the leading "data:" from input.
      46                 :        348 :  *  4. Let position point at the start of input.
      47                 :        348 :  *  5. Let mimeType be the result of collecting a sequence of code points that are not equal to U+002C (,), given position.
      48                 :        348 :  *  6. Strip leading and trailing ASCII whitespace from mimeType.
      49                 :        348 :  *     Note: This will only remove U+0020 SPACE code points, if any.
      50                 :        348 :  *  7. If position is past the end of input, then return failure.
      51                 :        348 :  *  8. Advance position by 1.
      52                 :        348 :  *  9. Let encodedBody be the remainder of input.
      53                 :        348 :  * 10. Let body be the percent-decoding of encodedBody.
      54                 :        348 :  * 11. If mimeType ends with U+003B (;), followed by zero or more U+0020 SPACE, followed by an ASCII case-insensitive match for "base64", then:
      55                 :        348 :  *     1. Let stringBody be the isomorphic decode of body.
      56                 :        348 :  *     2. Set body to the forgiving-base64 decode of stringBody.
      57                 :        348 :  *     3. If body is failure, then return failure.
      58                 :        348 :  *     4. Remove the last 6 code points from mimeType.
      59                 :        348 :  *     5. Remove trailing U+0020 SPACE code points from mimeType, if any.
      60                 :        348 :  *     6. Remove the last U+003B (;) from mimeType.
      61                 :        348 :  * 12. If mimeType starts with ";", then prepend "text/plain" to mimeType.
      62                 :        348 :  * 13. Let mimeTypeRecord be the result of parsing mimeType.
      63                 :        348 :  * 14. If mimeTypeRecord is failure, then set mimeTypeRecord to text/plain;charset=US-ASCII.
      64                 :        348 :  * 15. Return a new data: URL struct whose MIME type is mimeTypeRecord and body is body.
      65                 :        348 :  *
      66                 :        348 :  * @func   dataURLProcessor
      67                 :        348 :  * @static
      68                 :        348 :  * @param  {URL} dataURL - URL object.
      69                 :        348 :  * @return {object|string} An object of mimeType and body, or 'failure' string.
      70                 :        348 :  * @desc A dataURLProcessor function, as defined by {@link https://fetch.spec.whatwg.org/#data-url-processor WHATWG Fetch Living Standard}.
      71                 :        348 :  */
      72            [ + ]:        348 : export function dataURLProcessor(dataURL){
      73                 :         13 : 
      74                 :         13 :         // fetch.spec.1. Assert: dataURL’s scheme is "data".
      75                 :         13 :         assert(dataURL.protocol === 'data:');
      76                 :         13 : 
      77                 :         13 :         // fetch.spec.2. Let input be the result of running the URL
      78                 :         13 :         //               serializer on dataURL with exclude fragment
      79                 :         13 :         //               set to true.
      80                 :         13 :         let input = urlSerializer(dataURL, true);
      81                 :         13 : 
      82                 :         13 :         // fetch.spec.3. Remove the leading "data:" string from input.
      83                 :         13 :         input = input.slice(5);
      84                 :         13 : 
      85                 :         13 :         // fetch.spec.4. Let position point at the start of input.
      86                 :         13 :         const position = { position: 0 };
      87                 :         13 : 
      88                 :         13 :         // fetch.spec.5. Let mimeType be the result of collecting a
      89                 :         13 :         //               sequence of code points that are not equal
      90                 :         13 :         //               to U+002C (,), given position.
      91                 :         13 :         let mimeType = collectASequenceOfCodePointsFast(
      92                 :         13 :                 ',',
      93                 :         13 :                 input,
      94                 :         13 :                 position
      95                 :         13 :         );
      96                 :         13 : 
      97                 :         13 :         // fetch.spec.6. Strip leading and trailing ASCII whitespace from mimeType.
      98                 :         13 :         //               Note: This will only remove U+0020 SPACE code points, if any.
      99                 :         13 :         //               Undici implementation note: we need to store the length because
     100                 :         13 :         //               if the mimetype has spaces removed, the wrong amount will be
     101                 :         13 :         //               sliced from the input in step #9.
     102                 :         13 :         const mimeTypeLength = mimeType.length;
     103                 :         13 :         mimeType = mimeType.replace(/^(\u0020)+|(\u0020)+$/g, '');
     104                 :         13 : 
     105                 :         13 :         // fetch.spec.7. If position is past the end of input, then return failure
     106                 :         13 :         if(position.position >= input.length)
     107       [ + ][ + ]:         13 :                 return 'failure';
     108                 :         11 : 
     109                 :         11 :         // fetch.spec.8. Advance position by 1.
     110                 :         11 :         position.position++;
     111                 :         11 : 
     112                 :         11 :         // fetch.spec.9. Let encodedBody be the remainder of input.
     113                 :         11 :         const encodedBody = input.slice(mimeTypeLength + 1);
     114                 :         11 : 
     115                 :         11 :         // fetch.spec.10. Let body be the percent-decoding of encodedBody.
     116                 :         11 :         let body = stringPercentDecode(encodedBody);
     117                 :         11 : 
     118                 :         11 :         // fetch.spec.11. If mimeType ends with U+003B (;), followed by
     119                 :         11 :         //                zero or more U+0020 SPACE, followed by an ASCII
     120                 :         11 :         //                case-insensitive match for "base64", then:
     121            [ + ]:         13 :         if(/;(\u0020){0,}base64$/i.test(mimeType)){
     122                 :          1 : 
     123                 :          1 :                 // fetch.spec.11.1. Let stringBody be the isomorphic decode of body.
     124                 :          1 :                 const stringBody = isomorphicDecode(body);
     125                 :          1 : 
     126                 :          1 :                 // fetch.spec.11.2. Set body to the forgiving-base64 decode of stringBody.
     127                 :          1 :                 body = forgivingBase64(stringBody);
     128                 :          1 : 
     129                 :          1 :                 // fetch.spec.11.3. If body is failure, then return failure.
     130                 :          1 :                 if (body === 'failure') return 'failure';
     131                 :          1 : 
     132                 :          1 :                 // fetch.spec.11.4. Remove the last 6 code points from mimeType.
     133                 :          1 :                 mimeType = mimeType.slice(0, -6);
     134                 :          1 : 
     135                 :          1 :                 // fetch.spec.11.5. Remove trailing U+0020 SPACE code points from mimeType, if any.
     136                 :          1 :                 mimeType = mimeType.replace(/(\u0020)+$/, '');
     137                 :          1 : 
     138                 :          1 :                 // fetch.spec.11.6. Remove the last U+003B (;) code point from mimeType.
     139                 :          1 :                 mimeType = mimeType.slice(0, -1);
     140            [ + ]:          1 :         }
     141                 :         11 : 
     142                 :         11 :         // fetch.spec.12. If mimeType starts with U+003B (;), then prepend "text/plain" to mimeType.
     143                 :         11 :         if(mimeType.startsWith(';'))
     144       [ + ][ - ]:         13 :                 mimeType = 'text/plain' + mimeType;
     145                 :         11 : 
     146                 :         11 :         // fetch.spec.13. Let mimeTypeRecord be the result of parsing mimeType.
     147                 :         11 :         let mimeTypeRecord = parseMIMEType(mimeType);
     148                 :         11 : 
     149                 :         11 :         // fetch.spec.14. If mimeTypeRecord is failure, then set
     150                 :         11 :         //                mimeTypeRecord to text/plain;charset=US-ASCII.
     151                 :         11 :         if(mimeTypeRecord === 'failure')
     152       [ + ][ + ]:         13 :                 mimeTypeRecord = parseMIMEType('text/plain;charset=US-ASCII');
     153                 :         11 : 
     154                 :         11 :         // fetch.spec.11.15. Return a new data: URL struct whose MIME
     155                 :         11 :         //                   type is mimeTypeRecord and body is body.
     156                 :         11 : 
     157                 :         11 :         return { mimeType: mimeTypeRecord, body };
     158                 :         13 : }
     159                 :        348 : 
     160                 :        348 : /*
     161                 :        348 :  * URL serializer
     162                 :        348 :  * url: https://url.spec.whatwg.org/#concept-url-serializer
     163                 :        348 :  *
     164                 :        348 :  * takes a URL url, with an optional boolean exclude fragment (default false)
     165                 :        348 :  * They return an ASCII string.
     166                 :        348 :  *
     167                 :        348 :  * @func   urlSerializer
     168                 :        348 :  * @param  {URL} url - URL object.
     169                 :        348 :  * @param  {boolean} excludeFragment - (Optional) Boolean parameter, defaulting to false, to exclude fragment.
     170                 :        348 :  * @return {string} ASCII string.
     171                 :        348 :  * @desc   A URL serializer function, as defined by {@link https://url.spec.whatwg.org/#concept-url-serializer WHATWG URL Living Standard}.
     172                 :        348 :  */
     173            [ + ]:         13 : function urlSerializer(url, excludeFragment = false){
     174                 :         13 : 
     175                 :         13 :         if(!excludeFragment)
     176            [ - ]:         13 :         return url.href;
     177                 :         13 : 
     178                 :         13 :         const hash = url.href.lastIndexOf('#');
     179                 :         13 :         if(hash === -1)
     180            [ - ]:         13 :                 return url.href;
     181                 :          0 : 
     182                 :          0 :         return url.href.slice(0, hash);
     183                 :         13 : }
     184                 :        348 : 
     185                 :        348 : /*
     186                 :        348 :  * collect a sequence of code points
     187                 :        348 :  * url: https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
     188                 :        348 :  *
     189                 :        348 :  * @func   collectASequenceOfCodePoints
     190                 :        348 :  * @param  {function} condition - (string) => boolean.
     191                 :        348 :  * @param  {string} input
     192                 :        348 :  * @param  {{ position: number }} position
     193                 :        348 :  * @return {string} Sequence of code points.
     194                 :        348 :  * @desc   A function To collect a sequence of code points meeting a condition from a string input, as defined by {@link https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points WHATWG Infra Living Standard}.
     195                 :        348 :  */
     196            [ + ]:          8 : function collectASequenceOfCodePoints(condition, input, position){
     197                 :          8 : 
     198                 :          8 :         // infra.spec.1. Let result be the empty string.
     199                 :          8 :         let result = '';
     200                 :          8 : 
     201                 :          8 :         // infra.spec.2. While position doesn’t point past the end of input and the
     202                 :          8 :         //               code point at position within input meets the condition condition:
     203       [ + ][ + ]:          8 :         while (position.position < input.length && condition(input[position.position])){
     204                 :         28 : 
     205                 :         28 :                 // infra.spec.2.1. Append that code point to the end of result.
     206                 :         28 :                 result += input[position.position];
     207                 :         28 : 
     208                 :         28 :                 // infra.spec.2.2. Advance position by 1.
     209                 :         28 :                 position.position++;
     210                 :         28 :         }
     211                 :          8 : 
     212                 :          8 :         // infra.spec.3. Return result.
     213                 :          8 :         return result;
     214                 :          8 : }
     215                 :        348 : 
     216                 :        348 : /*
     217                 :        348 :  * A faster collectASequenceOfCodePoints that only works when comparing a single character.
     218                 :        348 :  *
     219                 :        348 :  *   @param {string} char
     220                 :        348 :  *   @param {string} input
     221                 :        348 :  *   @param {{ position: number }} position
     222                 :        348 :  */
     223            [ + ]:         49 : function collectASequenceOfCodePointsFast (char, input, position){
     224                 :         49 : 
     225                 :         49 :         const idx = input.indexOf(char, position.position);
     226                 :         49 :         const start = position.position;
     227                 :         49 : 
     228            [ + ]:         49 :         if(idx === -1){
     229                 :         20 :                 position.position = input.length;
     230                 :         20 :                 return input.slice(start);
     231            [ + ]:         20 :         }
     232                 :         28 : 
     233                 :         28 :         position.position = idx;
     234                 :         28 : 
     235                 :         28 :         return input.slice(start, position.position);
     236                 :         49 : }
     237                 :        348 : 
     238                 :        348 : /*
     239                 :        348 :  * To percent-decode a scalar value string input:
     240                 :        348 :  * url: https://url.spec.whatwg.org/#string-percent-decode
     241                 :        348 :  *
     242                 :        348 :  * @param {string} input
     243                 :        348 :  */
     244            [ + ]:         12 : function stringPercentDecode (input) {
     245                 :         12 : 
     246                 :         12 :         const encoder = new TextEncoder();
     247                 :         12 : 
     248                 :         12 :         // url.spec.1. Let bytes be the UTF-8 encoding of input.
     249                 :         12 :         const bytes = encoder.encode(input);
     250                 :         12 : 
     251                 :         12 :         // url.spec.2. Return the percent-decoding of bytes.
     252                 :         12 :         return percentDecode(bytes);
     253                 :         12 : }
     254                 :        348 : 
     255                 :        348 : /*
     256                 :        348 :  * percent-decode
     257                 :        348 :  * url: https://url.spec.whatwg.org/#percent-decode
     258                 :        348 :  *
     259                 :        348 :  * To percent-decode a byte sequence input,
     260                 :        348 :  *
     261                 :        348 :  * @param {Uint8Array} input
     262                 :        348 :  */
     263            [ + ]:         12 : function percentDecode(input){
     264                 :         12 : 
     265                 :         12 :         // url.spec.1. Let output be an empty byte sequence.
     266                 :         12 :         const output = [];
     267                 :         12 : 
     268                 :         12 :         // url.spec.2. For each byte byte in input:
     269            [ + ]:         12 :         for(let i = 0; i < input.length; i++){
     270                 :        162 :                 const byte = input[i];
     271                 :        162 : 
     272                 :        162 :                 // url.spec.2.1. If byte is not 0x25 (%), then append byte to output.
     273            [ + ]:        162 :                 if (byte !== 0x25) {
     274                 :         20 :                         output.push(byte);
     275            [ + ]:         20 :                 }
     276                 :          1 :                 // url.spec.2.2. Otherwise, if byte is 0x25 (%) and the next two bytes
     277                 :          1 :                 //               after byte in input are not in the ranges
     278                 :          1 :                 //               0x30 (0) to 0x39 (9), 0x41 (A) to 0x46 (F),
     279                 :          1 :                 //               and 0x61 (a) to 0x66 (f), all inclusive, append byte
     280                 :          1 :                 //               to output.
     281                 :          1 :                 else
     282                 :          1 :                 if(
     283                 :          1 :                         byte === 0x25 &&
     284                 :          1 :                         !/^[0-9A-Fa-f]{2}$/i.test(String.fromCharCode(input[i + 1], input[i + 2]))
     285                 :          1 :                 ){
     286                 :          1 :                         output.push(0x25);
     287                 :          1 :                 }
     288                 :          1 :                 // url.spec.2.3. Otherwise:
     289                 :          1 :                 else {
     290                 :          1 :                         // url.spec.2.3.1. Let bytePoint be the two bytes after byte in input,
     291                 :          1 :                         //                 decoded, and then interpreted as hexadecimal number.
     292                 :          1 : 
     293                 :          1 :                         const nextTwoBytes = String.fromCharCode(input[i + 1], input[i + 2]);
     294                 :          1 :                         const bytePoint = Number.parseInt(nextTwoBytes, 16);
     295                 :          1 : 
     296                 :          1 :                         // url.spec.2.3.2. Append a byte whose value is bytePoint to output.
     297                 :          1 :                         output.push(bytePoint);
     298                 :          1 : 
     299                 :          1 :                         // url.spec.2.3.3. Skip the next two bytes in input.
     300                 :          1 :                         i += 2;
     301                 :          1 :                 }
     302                 :        162 :         }
     303                 :         12 : 
     304                 :         12 :         // url.spec.3. Return output.
     305                 :         12 :         return Uint8Array.from(output);
     306                 :         12 : }
     307                 :        348 : 
     308                 :        348 : /**
     309                 :        348 :  * parse a MIME type,
     310                 :        348 :  * url: https://mimesniff.spec.whatwg.org/#parse-a-mime-type
     311                 :        348 :  *
     312                 :        348 :  * @param {string} input
     313                 :        348 :  *
     314                 :        348 :  * @func   parseMIMEType
     315                 :        348 :  * @static
     316                 :        348 :  * @param  {string} input - String input.
     317                 :        348 :  * @return {object} Serialized MIME type record.
     318                 :        348 :  * @desc   Parse a MIME type, given a string input, as defined by {@link https://mimesniff.spec.whatwg.org/#parse-a-mime-type WHATWG MIME Sniffing Living Standard}.
     319                 :        348 :  */
     320            [ + ]:        348 : export function parseMIMEType(input){
     321                 :         17 : 
     322                 :         17 :         // mimesniff.spec.1. Remove any leading and trailing HTTP whitespace
     323                 :         17 :         //                   from input.
     324                 :         17 :         input = input.trim();
     325                 :         17 : 
     326                 :         17 :         // mimesniff.spec.2. Let position be a position variable for input,
     327                 :         17 :         //                   initially pointing at the start of input.
     328                 :         17 :         const position = { position: 0 };
     329                 :         17 : 
     330                 :         17 :         // mimesniff.spec.3. Let type be the result of collecting a sequence
     331                 :         17 :         //                   of code points that are not U+002F (/) from
     332                 :         17 :         //                   input, given position.
     333                 :         17 :         const type = collectASequenceOfCodePointsFast(
     334                 :         17 :                 '/',
     335                 :         17 :                 input,
     336                 :         17 :                 position
     337                 :         17 :         );
     338                 :         17 : 
     339                 :         17 :         // mimesniff.spec.4. If type is the empty string or does not solely
     340                 :         17 :         //                   contain HTTP token code points, then return failure.
     341            [ + ]:         17 :         if(type.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(type))
     342       [ + ][ + ]:         17 :                 return 'failure';
     343                 :         15 : 
     344                 :         15 :         // mimesniff.spec.5. If position is past the end of input,
     345                 :         15 :         //                   then return failure
     346                 :         15 :         if (position.position > input.length)
     347       [ - ][ + ]:         17 :                 return 'failure';
     348                 :         15 : 
     349                 :         15 :         // mimesniff.spec.6. Advance position by 1. (This skips past U+002F (/).)
     350                 :         15 :         position.position++;
     351                 :         15 : 
     352                 :         15 :         // mimesniff.spec.7. Let subtype be the result of collecting a sequence of
     353                 :         15 :         //                   code points that are not U+003B (;) from input, given
     354                 :         15 :         //                   position.
     355                 :         15 :         let subtype = collectASequenceOfCodePointsFast(
     356                 :         15 :                 ';',
     357                 :         15 :                 input,
     358                 :         15 :                 position
     359                 :         15 :         );
     360                 :         15 : 
     361                 :         15 :         // mimesniff.spec.8. Remove any trailing HTTP whitespace from subtype.
     362                 :         15 :         subtype = subtype.trimEnd();
     363                 :         15 : 
     364                 :         15 :         // mimesniff.spec.9. If subtype is the empty string or does not solely
     365                 :         15 :         //                   contain HTTP token code points, then return failure.
     366            [ + ]:         17 :         if(subtype.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(subtype))
     367       [ + ][ + ]:         17 :                 return 'failure';
     368                 :         13 : 
     369                 :         13 :         // mimesniff.spec.10. Let mimeType be a new MIME type record whose type
     370                 :         13 :         //                    is type, in ASCII lowercase, and subtype is subtype,
     371                 :         13 :         //                    in ASCII lowercase.
     372                 :         13 :         const mimeType = {
     373                 :         13 :                 type:       type.toLowerCase(),
     374                 :         13 :                 subtype:    subtype.toLowerCase(),
     375                 :         13 :                 parameters: new Map(),
     376                 :         13 :                 essence:    `${type}/${subtype}`
     377                 :         13 :         };
     378                 :         13 : 
     379                 :         13 :         // mimesniff.spec.11. While position is not past the end of input:
     380            [ + ]:         17 :         while (position.position < input.length){
     381                 :          4 : 
     382                 :          4 :                 // mimesniff.spec.11.1. Advance position by 1. (This skips past U+003B (;).)
     383                 :          4 :                 position.position++;
     384                 :          4 : 
     385                 :          4 :                 // mimesniff.spec.11.2. Collect a sequence of code points that are HTTP
     386                 :          4 :                 //                      whitespace from input given position.
     387                 :          4 :                 collectASequenceOfCodePoints(
     388            [ + ]:          4 :                         char => HTTP_WHITESPACE_REGEX.test(char),
     389                 :          4 :                         input,
     390                 :          4 :                         position
     391                 :          4 :                 );
     392                 :          4 : 
     393                 :          4 :                 // mimesniff.spec.11.3. Let parameterName be the result of collecting a
     394                 :          4 :                 //                      sequence of code points that are not U+003B (;)
     395                 :          4 :                 //                      or U+003D (=) from input, given position.
     396                 :          4 :                 let parameterName = collectASequenceOfCodePoints(
     397            [ + ]:          4 :                         (char) => char !== ';' && char !== '=',
     398                 :          4 :                         input,
     399                 :          4 :                         position
     400                 :          4 :                 );
     401                 :          4 : 
     402                 :          4 :                 // mimesniff.spec.11.4. Set parameterName to parameterName,
     403                 :          4 :                 //                      in ASCII lowercase.
     404                 :          4 :                 parameterName = parameterName.toLowerCase();
     405                 :          4 : 
     406                 :          4 :                 // mimesniff.spec.11.5. If position is not past the end of input, then:
     407                 :          4 :                 if(position.position < input.length){
     408                 :          4 :                         // mimesniff.spec.11.5.1. If the code point at position within input is
     409                 :          4 :                         //                        U+003B (;), then continue.
     410                 :          4 :                         if(input[position.position] === ';')
     411            [ - ]:          4 :                                 continue;
     412                 :          4 : 
     413                 :          4 :                         // mimesniff.spec.11.5.2. Advance position by 1. (This skips past U+003D (=).)
     414                 :          4 :                         position.position++;
     415                 :          4 :                 }
     416                 :          4 : 
     417                 :          4 :                 // mimesniff.spec.11.6. If position is past the end of input, then break.
     418                 :          4 :                 if(position.position > input.length)
     419            [ - ]:          4 :                         break;
     420                 :          4 : 
     421                 :          4 :                 // mimesniff.spec.11.7. Let parameterValue be null.
     422                 :          4 :                 let parameterValue = null;
     423                 :          4 : 
     424                 :          4 :                 // mimesniff.spec.11.8. If the code point at position within input is
     425                 :          4 :                 //                      U+0022 ("), then:
     426            [ - ]:          4 :                 if(input[position.position] === '"'){
     427                 :          0 : 
     428                 :          0 :                         // mimesniff.spec.11.8.1. Set parameterValue to the result of collecting
     429                 :          0 :                         //                        an HTTP quoted string from input, given position
     430                 :          0 :                         //                        and the extract-value flag.
     431                 :          0 :                         parameterValue = collectAnHTTPQuotedString(input, position, true);
     432                 :          0 : 
     433                 :          0 :                         // mimesniff.spec.11.8.2. Collect a sequence of code points that are not
     434                 :          0 :                         //                        U+003B (;) from input, given position.
     435                 :          0 :                         collectASequenceOfCodePointsFast(
     436                 :          0 :                                 ';',
     437                 :          0 :                                 input,
     438                 :          0 :                                 position
     439                 :          0 :                         );
     440                 :          0 :                 }
     441                 :          4 :                 // mimesniff.spec.11.9. Otherwise:
     442                 :          4 :                 else{
     443                 :          4 :                         // mimesniff.spec.11.9.1. Set parameterValue to the result of collecting
     444                 :          4 :                         //                        a sequence of code points that are not U+003B (;)
     445                 :          4 :                         //                        from input, given position.
     446                 :          4 :                         parameterValue = collectASequenceOfCodePointsFast(
     447                 :          4 :                                 ';',
     448                 :          4 :                                 input,
     449                 :          4 :                                 position
     450                 :          4 :                         );
     451                 :          4 : 
     452                 :          4 :                         // mimesniff.spec.11.9.2. Remove any trailing HTTP whitespace from parameterValue.
     453                 :          4 :                         //                        Note: it says "trailing" whitespace; leading is fine.
     454                 :          4 :                         parameterValue = parameterValue.trimEnd();
     455                 :          4 : 
     456                 :          4 :                         // mimesniff.spec.11.9.3. If parameterValue is the empty string, then continue.
     457                 :          4 :                         if(parameterValue.length === 0)
     458            [ - ]:          4 :                                 continue;
     459                 :          4 :                 }
     460                 :          4 : 
     461                 :          4 :                 // mimesniff.spec.11.10. If all of the following are true
     462                 :          4 :                 //                       - parameterName is not the empty string
     463                 :          4 :                 //                       - parameterName solely contains HTTP token code points
     464                 :          4 :                 //                       - parameterValue solely contains HTTP quoted-string token code points
     465                 :          4 :                 //                       - mimeType’s parameters[parameterName] does not exist
     466                 :          4 :                 //                       then set mimeType’s parameters[parameterName] to parameterValue.
     467                 :          4 :                 if(
     468                 :          4 :                         parameterName.length !== 0                      &&
     469                 :          4 :                         HTTP_TOKEN_CODEPOINTS.test(parameterName)       &&
     470                 :          4 :                         !HTTP_QUOTED_STRING_TOKENS.test(parameterValue) &&
     471                 :          4 :                         !mimeType.parameters.has(parameterName)
     472                 :          4 :                 ) {
     473                 :          4 :                         mimeType.parameters.set(parameterName, parameterValue);
     474                 :          4 :                 }
     475            [ + ]:          4 :         }
     476                 :         13 : 
     477                 :         13 :         // mimesniff.spec.12. Return mimeType.
     478                 :         13 :         return mimeType;
     479                 :         17 : }
     480                 :        348 : 
     481                 :        348 : /*
     482                 :        348 :  * forgiving-base64 decode
     483                 :        348 :  * url: https://infra.spec.whatwg.org/#forgiving-base64-decode
     484                 :        348 :  *
     485                 :        348 :  * @param {string} data
     486                 :        348 :  */
     487            [ + ]:          2 : function forgivingBase64 (data){
     488                 :          2 : 
     489                 :          2 :         // infra.spec.1. Remove all ASCII whitespace from data.
     490                 :          2 :         data = data.replace(/[\u0009\u000A\u000C\u000D\u0020]/g, '');
     491                 :          2 : 
     492                 :          2 :         // infra.spec.2. If data’s code point length divides by 4 leaving
     493                 :          2 :         //               no remainder, then:
     494                 :          2 :         if(data.length % 4 === 0){
     495                 :          2 :                 // infra.spec.2.1. If data ends with one or two U+003D (=) code points,
     496                 :          2 :                 //                 then remove them from data.
     497                 :          2 :                 data = data.replace(/=?=$/, '');
     498                 :          2 :         }
     499                 :          2 : 
     500                 :          2 :         // infra.spec.3. If data’s code point length divides by 4 leaving
     501                 :          2 :         //               a remainder of 1, then return failure.
     502                 :          2 :         if(data.length % 4 === 1){
     503                 :          2 :                 return 'failure';
     504                 :          2 :         }
     505                 :          2 : 
     506                 :          2 :         // infra.spec.4. If data contains a code point that is not one of
     507                 :          2 :         //               U+002B (+)
     508                 :          2 :         //                   U+002F (/)
     509                 :          2 :         //                   ASCII alphanumeric
     510                 :          2 :         //               then return failure.
     511            [ - ]:          2 :         if(/[^+/0-9A-Za-z]/.test(data)){
     512                 :          0 :                 return 'failure';
     513                 :          0 :         }
     514                 :          2 : 
     515                 :          2 :         const binary = atob(data);
     516                 :          2 :         const bytes = new Uint8Array(binary.length);
     517                 :          2 : 
     518            [ + ]:          2 :         for (let byte = 0; byte < binary.length; byte++) {
     519                 :         13 :                 bytes[byte] = binary.charCodeAt(byte);
     520                 :         13 :         }
     521                 :          2 : 
     522                 :          2 :         return bytes;
     523                 :          2 : }
     524                 :        348 : 
     525                 :        348 : /*
     526                 :        348 :  * collect an HTTP quoted string
     527                 :        348 :  * url: https://fetch.spec.whatwg.org/#collect-an-http-quoted-string
     528                 :        348 :  *
     529                 :        348 :  * @param {string} input
     530                 :        348 :  * @param {{ position: number }} position
     531                 :        348 :  * @param {boolean?} extractValue
     532                 :        348 :  */
     533                 :          0 : function collectAnHTTPQuotedString (input, position, extractValue) {
     534                 :          0 : 
     535                 :          0 :         // fetch.spec.1. Let positionStart be position.
     536                 :          0 :         const positionStart = position.position;
     537                 :          0 : 
     538                 :          0 :         // fetch.spec.2. Let value be the empty string.
     539                 :          0 :         let value = '';
     540                 :          0 : 
     541                 :          0 :         // fetch.spec.3. Assert: the code point at position within input
     542                 :          0 :         //               is U+0022 (").
     543                 :          0 :         assert(input[position.position] === '"');
     544                 :          0 : 
     545                 :          0 :         // fetch.spec.4. Advance position by 1.
     546                 :          0 :         position.position++;
     547                 :          0 : 
     548                 :          0 :         // fetch.spec.5. While true:
     549                 :          0 :         while(true){
     550                 :          0 :                 // fetch.spec.5.1. Append the result of collecting a sequence of code points
     551                 :          0 :                 //                 that are not U+0022 (") or U+005C (\) from input, given
     552                 :          0 :                 //                 position, to value.
     553                 :          0 :                 value += collectASequenceOfCodePoints(
     554                 :          0 :                         (char) => char !== '"' && char !== '\\',
     555                 :          0 :                         input,
     556                 :          0 :                         position
     557                 :          0 :                 );
     558                 :          0 : 
     559                 :          0 :                 // fetch.spec.5.2. If position is past the end of input, then break.
     560                 :          0 :                 if(position.position >= input.length)
     561                 :          0 :                         break;
     562                 :          0 : 
     563                 :          0 :                 // fetch.spec.5.3. Let quoteOrBackslash be the code point at position
     564                 :          0 :                 //                 within input.
     565                 :          0 :                 const quoteOrBackslash = input[position.position];
     566                 :          0 : 
     567                 :          0 :                 // fetch.spec.5.4. Advance position by 1.
     568                 :          0 :                 position.position++;
     569                 :          0 : 
     570                 :          0 :                 // fetch.spec.5.5. If quoteOrBackslash is U+005C (\), then:
     571                 :          0 :                 if(quoteOrBackslash === '\\'){
     572                 :          0 :                         // fetch.spec.5.5.1. If position is past the end of input, then append
     573                 :          0 :                         //                   U+005C (\) to value and break.
     574                 :          0 :                         if (position.position >= input.length) {
     575                 :          0 :                                 value += '\\'
     576                 :          0 :                                 break
     577                 :          0 :                         }
     578                 :          0 : 
     579                 :          0 :                         // fetch.spec.5.5.2. Append the code point at position within input to value.
     580                 :          0 :                         value += input[position.position];
     581                 :          0 : 
     582                 :          0 :                         // fetch.spec.5.5.3. Advance position by 1.
     583                 :          0 :                         position.position++;
     584                 :          0 :                 }
     585                 :          0 :                 // fetch.spec.5.6. Otherwise:
     586                 :          0 :                 else{
     587                 :          0 :                         // fetch.spec.5.6.1. Assert: quoteOrBackslash is U+0022 (").
     588                 :          0 :                         assert(quoteOrBackslash === '"');
     589                 :          0 : 
     590                 :          0 :                         // fetch.spec.5.6.2. Break.
     591                 :          0 :                         break;
     592                 :          0 :                 }
     593                 :          0 :         }
     594                 :          0 : 
     595                 :          0 :         // fetch.spec.6. If the extract-value flag is set, then return value.
     596                 :          0 :         if(extractValue)
     597                 :          0 :                 return value;
     598                 :          0 : 
     599                 :          0 :         // fetch.spec.7. Return the code points from positionStart to position,
     600                 :          0 :         //               inclusive, within input.
     601                 :          0 :         return input.slice(positionStart, position.position)
     602                 :          0 : }
     603                 :        348 : 
     604                 :        348 : /**
     605                 :        348 :  * serialize a MIME type
     606                 :        348 :  * url: https://mimesniff.spec.whatwg.org/#serialize-a-mime-type
     607                 :        348 :  *
     608                 :        348 :  * @func   serializeAMimeType
     609                 :        348 :  * @static
     610                 :        348 :  * @param  {object} mimeType - MIME type record.
     611                 :        348 :  * @return {string} Serialized MIME type record.
     612                 :        348 :  * @desc   Serialize a MIME type, as defined by {@link https://mimesniff.spec.whatwg.org/#serialize-a-mime-type WHATWG MIME Sniffing Living Standard}.
     613                 :        348 :  */
     614            [ + ]:        348 : export function serializeAMimeType(mimeType){
     615                 :         13 : 
     616                 :         13 :         assert(mimeType !== 'failure');
     617                 :         13 : 
     618                 :         13 :         const { type, subtype, parameters } = mimeType;
     619                 :         13 : 
     620                 :         13 :         // mimesniff.spec.1. Let serialization be the concatenation of mimeType’s
     621                 :         13 :         //                           type, U+002F (/), and mimeType’s subtype.
     622                 :         13 :         let serialization = `${type}/${subtype}`;
     623                 :         13 : 
     624                 :         13 :         // mimesniff.spec.2. For each name → value of mimeType’s parameters:
     625            [ + ]:         13 :         for(let [name, value] of parameters.entries()){
     626                 :          4 : 
     627                 :          4 :                 // mimesniff.spec.2.1. Append U+003B (;) to serialization.
     628                 :          4 :                 serialization += ';'
     629                 :          4 : 
     630                 :          4 :                 // mimesniff.spec.2.2. Append name to serialization.
     631                 :          4 :                 serialization += name;
     632                 :          4 : 
     633                 :          4 :                 // mimesniff.spec.2.3. Append U+003D (=) to serialization.
     634                 :          4 :                 serialization += '=';
     635                 :          4 : 
     636                 :          4 :                 // mimesniff.spec.2.4. If value does not solely contain HTTP token code
     637                 :          4 :                 //                             points or value is the empty string, then:
     638            [ - ]:          4 :                 if(!isValidHTTPToken(value)){
     639                 :          0 :                         // mimesniff.spec.2.4.1. Precede each occurence of U+0022 (") or
     640                 :          0 :                         //                               U+005C (\) in value with U+005C (\).
     641                 :          0 :                         value = value.replace(/(\\|")/g, '\\$1');
     642                 :          0 : 
     643                 :          0 :                         // mimesniff.spec.2.4.2. Prepend U+0022 (") to value.
     644                 :          0 :                         value = '"' + value;
     645                 :          0 : 
     646                 :          0 :                         // mimesniff.spec.2.4.3. Append U+0022 (") to value.
     647                 :          0 :                         value += '"';
     648                 :          0 :                 }
     649                 :          4 : 
     650                 :          4 :                 // mimesniff.spec.2.5. Append value to serialization.
     651                 :          4 :                 serialization += value;
     652                 :          4 :         }
     653                 :         13 : 
     654                 :         13 :         // mimesniff.spec.3. Return serialization.
     655                 :         13 :         return serialization;
     656                 :         13 : }
     657                 :        348 : 
     658                 :        348 : const MAXIMUM_ARGUMENT_LENGTH = 65535
     659                 :        348 : 
     660                 :        348 : /*
     661                 :        348 :  * isomorphic decode
     662                 :        348 :  * url: https://infra.spec.whatwg.org/#isomorphic-decode
     663                 :        348 :  *
     664                 :        348 :  * To isomorphic decode a byte sequence input, return a string whose code point length
     665                 :        348 :  * is equal to input’s length and whose code points have the same values as the values
     666                 :        348 :  * of input’s bytes, in the same order.
     667                 :        348 :  *
     668                 :        348 :  * @param {number[]|Uint8Array} input
     669                 :        348 :  */
     670            [ + ]:          2 : function isomorphicDecode(input){
     671                 :          2 : 
     672                 :          2 :         if(input.length < MAXIMUM_ARGUMENT_LENGTH)
     673            [ - ]:          2 :                 return String.fromCharCode(...input);
     674                 :          0 : 
     675                 :          0 :         return input.reduce((previous, current) => previous + String.fromCharCode(current), '');
     676                 :          2 : }
     677                 :        348 : 
     678                 :        348 : // RFC 7230, Section 3.2.6.
     679                 :        348 : // https://github.com/chromium/chromium/blob/d7da0240cae77824d1eda25745c4022757499131/third_party/blink/renderer/platform/network/http_parsers.cc#L321
     680                 :        348 : 
     681            [ + ]:          4 : function isValidHTTPToken(characters){
     682                 :          4 : 
     683                 :          4 :         if(!characters || typeof characters !== 'string')
     684            [ - ]:          4 :                 return false;
     685                 :          4 : 
     686            [ + ]:          4 :         for(let i = 0; i < characters.length; ++i){
     687                 :         27 :                 const c = characters.charCodeAt(i);
     688                 :         27 :                 if(c > 0x7f || !isTokenChar(c))
     689            [ - ]:         27 :                         return false;
     690                 :         27 :         }
     691                 :          4 : 
     692                 :          4 :         return true;
     693                 :          4 : }
     694                 :        348 : 
     695            [ + ]:         27 : function isTokenChar(c){
     696                 :         27 :         return !(
     697                 :         27 :                 c >= 0x7f ||
     698                 :         27 :                 c <= 0x20 ||
     699                 :         27 :                 c === '(' ||
     700                 :         27 :                 c === ')' ||
     701                 :         27 :                 c === '<' ||
     702                 :         27 :                 c === '>' ||
     703                 :         27 :                 c === '@' ||
     704                 :         27 :                 c === ',' ||
     705                 :         27 :                 c === ';' ||
     706                 :         27 :                 c === ':' ||
     707                 :         27 :                 c === '\\' ||
     708                 :         27 :                 c === '"' ||
     709                 :         27 :                 c === '/' ||
     710                 :         27 :                 c === '[' ||
     711                 :         27 :                 c === ']' ||
     712                 :         27 :                 c === '?' ||
     713                 :         27 :                 c === '=' ||
     714                 :         27 :                 c === '{' ||
     715                 :         27 :                 c === '}'
     716                 :         27 :         );
     717                 :         27 : }

Generated by: LCOV version 1.14