| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.core; | 5 part of dart.core; |
| 6 | 6 |
| 7 // Frequently used character codes. | 7 // Frequently used character codes. |
| 8 const int _SPACE = 0x20; | 8 const int _SPACE = 0x20; |
| 9 const int _PERCENT = 0x25; | 9 const int _PERCENT = 0x25; |
| 10 const int _AMPERSAND = 0x26; | 10 const int _AMPERSAND = 0x26; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 * use an empty map for `queryParameters`. | 111 * use an empty map for `queryParameters`. |
| 112 * | 112 * |
| 113 * If both `query` and `queryParameters` are omitted or `null`, | 113 * If both `query` and `queryParameters` are omitted or `null`, |
| 114 * the URI has no query part. | 114 * the URI has no query part. |
| 115 * | 115 * |
| 116 * The fragment component is set through [fragment]. | 116 * The fragment component is set through [fragment]. |
| 117 * It should be a valid URI fragment, but invalid characters other than | 117 * It should be a valid URI fragment, but invalid characters other than |
| 118 * general delimiters, are escaped if necessary. | 118 * general delimiters, are escaped if necessary. |
| 119 * If `fragment` is omitted or `null`, the URI has no fragment part. | 119 * If `fragment` is omitted or `null`, the URI has no fragment part. |
| 120 */ | 120 */ |
| 121 factory Uri({String scheme, | 121 factory Uri( |
| 122 String userInfo, | 122 {String scheme, |
| 123 String host, | 123 String userInfo, |
| 124 int port, | 124 String host, |
| 125 String path, | 125 int port, |
| 126 Iterable<String> pathSegments, | 126 String path, |
| 127 String query, | 127 Iterable<String> pathSegments, |
| 128 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, | 128 String query, |
| 129 String fragment}) = _Uri; | 129 Map<String, dynamic /*String|Iterable<String>*/ > queryParameters, |
| 130 String fragment}) = _Uri; |
| 130 | 131 |
| 131 /** | 132 /** |
| 132 * Creates a new `http` URI from authority, path and query. | 133 * Creates a new `http` URI from authority, path and query. |
| 133 * | 134 * |
| 134 * Examples: | 135 * Examples: |
| 135 * | 136 * |
| 136 * ``` | 137 * ``` |
| 137 * // http://example.org/path?q=dart. | 138 * // http://example.org/path?q=dart. |
| 138 * new Uri.http("example.org", "/path", { "q" : "dart" }); | 139 * new Uri.http("example.org", "/path", { "q" : "dart" }); |
| 139 * | 140 * |
| (...skipping 14 matching lines...) Expand all Loading... |
| 154 * the created `Uri` has no authority, and isn't directly usable | 155 * the created `Uri` has no authority, and isn't directly usable |
| 155 * as an HTTP URL, which must have a non-empty host. | 156 * as an HTTP URL, which must have a non-empty host. |
| 156 * | 157 * |
| 157 * The `path` component is set from the [unencodedPath] | 158 * The `path` component is set from the [unencodedPath] |
| 158 * argument. The path passed must not be encoded as this constructor | 159 * argument. The path passed must not be encoded as this constructor |
| 159 * encodes the path. | 160 * encodes the path. |
| 160 * | 161 * |
| 161 * The `query` component is set from the optional [queryParameters] | 162 * The `query` component is set from the optional [queryParameters] |
| 162 * argument. | 163 * argument. |
| 163 */ | 164 */ |
| 164 factory Uri.http(String authority, | 165 factory Uri.http(String authority, String unencodedPath, |
| 165 String unencodedPath, | 166 [Map<String, String> queryParameters]) = _Uri.http; |
| 166 [Map<String, String> queryParameters]) = _Uri.http; | |
| 167 | 167 |
| 168 /** | 168 /** |
| 169 * Creates a new `https` URI from authority, path and query. | 169 * Creates a new `https` URI from authority, path and query. |
| 170 * | 170 * |
| 171 * This constructor is the same as [Uri.http] except for the scheme | 171 * This constructor is the same as [Uri.http] except for the scheme |
| 172 * which is set to `https`. | 172 * which is set to `https`. |
| 173 */ | 173 */ |
| 174 factory Uri.https(String authority, | 174 factory Uri.https(String authority, String unencodedPath, |
| 175 String unencodedPath, | 175 [Map<String, String> queryParameters]) = _Uri.https; |
| 176 [Map<String, String> queryParameters]) = _Uri.https; | |
| 177 | 176 |
| 178 /** | 177 /** |
| 179 * Creates a new file URI from an absolute or relative file path. | 178 * Creates a new file URI from an absolute or relative file path. |
| 180 * | 179 * |
| 181 * The file path is passed in [path]. | 180 * The file path is passed in [path]. |
| 182 * | 181 * |
| 183 * This path is interpreted using either Windows or non-Windows | 182 * This path is interpreted using either Windows or non-Windows |
| 184 * semantics. | 183 * semantics. |
| 185 * | 184 * |
| 186 * With non-Windows semantics the slash (`/`) is used to separate | 185 * With non-Windows semantics the slash (`/`) is used to separate |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 * If [mimeType] and/or [parameters] are supplied, they are added to the | 290 * If [mimeType] and/or [parameters] are supplied, they are added to the |
| 292 * created URI. If any of these contain characters that are not allowed | 291 * created URI. If any of these contain characters that are not allowed |
| 293 * in the data URI, the character is percent-escaped. If the character is | 292 * in the data URI, the character is percent-escaped. If the character is |
| 294 * non-ASCII, it is first UTF-8 encoded and then the bytes are percent | 293 * non-ASCII, it is first UTF-8 encoded and then the bytes are percent |
| 295 * encoded. An omitted [mimeType] in a data URI means `text/plain`, just | 294 * encoded. An omitted [mimeType] in a data URI means `text/plain`, just |
| 296 * as an omitted `charset` parameter defaults to meaning `US-ASCII`. | 295 * as an omitted `charset` parameter defaults to meaning `US-ASCII`. |
| 297 * | 296 * |
| 298 * To read the content back, use [UriData.contentAsString]. | 297 * To read the content back, use [UriData.contentAsString]. |
| 299 */ | 298 */ |
| 300 factory Uri.dataFromString(String content, | 299 factory Uri.dataFromString(String content, |
| 301 {String mimeType, | 300 {String mimeType, |
| 302 Encoding encoding, | 301 Encoding encoding, |
| 303 Map<String, String> parameters, | 302 Map<String, String> parameters, |
| 304 bool base64: false}) { | 303 bool base64: false}) { |
| 305 UriData data = new UriData.fromString(content, | 304 UriData data = new UriData.fromString(content, |
| 306 mimeType: mimeType, | 305 mimeType: mimeType, |
| 307 encoding: encoding, | 306 encoding: encoding, |
| 308 parameters: parameters, | 307 parameters: parameters, |
| 309 base64: base64); | 308 base64: base64); |
| 310 return data.uri; | 309 return data.uri; |
| 311 } | 310 } |
| 312 | 311 |
| 313 /** | 312 /** |
| 314 * Creates a `data:` URI containing an encoding of [bytes]. | 313 * Creates a `data:` URI containing an encoding of [bytes]. |
| 315 * | 314 * |
| 316 * Defaults to Base64 encoding the bytes, but if [percentEncoded] | 315 * Defaults to Base64 encoding the bytes, but if [percentEncoded] |
| 317 * is `true`, the bytes will instead be percent encoded (any non-ASCII | 316 * is `true`, the bytes will instead be percent encoded (any non-ASCII |
| 318 * or non-valid-ASCII-character byte is replaced by a percent encoding). | 317 * or non-valid-ASCII-character byte is replaced by a percent encoding). |
| 319 * | 318 * |
| 320 * To read the bytes back, use [UriData.contentAsBytes]. | 319 * To read the bytes back, use [UriData.contentAsBytes]. |
| 321 * | 320 * |
| 322 * It defaults to having the mime-type `application/octet-stream`. | 321 * It defaults to having the mime-type `application/octet-stream`. |
| 323 * The [mimeType] and [parameters] are added to the created URI. | 322 * The [mimeType] and [parameters] are added to the created URI. |
| 324 * If any of these contain characters that are not allowed | 323 * If any of these contain characters that are not allowed |
| 325 * in the data URI, the character is percent-escaped. If the character is | 324 * in the data URI, the character is percent-escaped. If the character is |
| 326 * non-ASCII, it is first UTF-8 encoded and then the bytes are percent | 325 * non-ASCII, it is first UTF-8 encoded and then the bytes are percent |
| 327 * encoded. | 326 * encoded. |
| 328 */ | 327 */ |
| 329 factory Uri.dataFromBytes(List<int> bytes, | 328 factory Uri.dataFromBytes(List<int> bytes, |
| 330 {mimeType: "application/octet-stream", | 329 {mimeType: "application/octet-stream", |
| 331 Map<String, String> parameters, | 330 Map<String, String> parameters, |
| 332 percentEncoded: false}) { | 331 percentEncoded: false}) { |
| 333 UriData data = new UriData.fromBytes(bytes, | 332 UriData data = new UriData.fromBytes(bytes, |
| 334 mimeType: mimeType, | 333 mimeType: mimeType, |
| 335 parameters: parameters, | 334 parameters: parameters, |
| 336 percentEncoded: percentEncoded); | 335 percentEncoded: percentEncoded); |
| 337 return data.uri; | 336 return data.uri; |
| 338 } | 337 } |
| 339 | 338 |
| 340 /** | 339 /** |
| 341 * The scheme component of the URI. | 340 * The scheme component of the URI. |
| 342 * | 341 * |
| 343 * Returns the empty string if there is no scheme component. | 342 * Returns the empty string if there is no scheme component. |
| 344 * | 343 * |
| 345 * A URI scheme is case insensitive. | 344 * A URI scheme is case insensitive. |
| 346 * The returned scheme is canonicalized to lowercase letters. | 345 * The returned scheme is canonicalized to lowercase letters. |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 607 * of a `data:` URI. | 606 * of a `data:` URI. |
| 608 */ | 607 */ |
| 609 UriData get data; | 608 UriData get data; |
| 610 | 609 |
| 611 /// Returns a hash code computed as `toString().hashCode`. | 610 /// Returns a hash code computed as `toString().hashCode`. |
| 612 /// | 611 /// |
| 613 /// This guarantees that URIs with the same normalized | 612 /// This guarantees that URIs with the same normalized |
| 614 int get hashCode; | 613 int get hashCode; |
| 615 | 614 |
| 616 /// A URI is equal to another URI with the same normalized representation. | 615 /// A URI is equal to another URI with the same normalized representation. |
| 617 bool operator==(Object other); | 616 bool operator ==(Object other); |
| 618 | 617 |
| 619 /// Returns the normalized string representation of the URI. | 618 /// Returns the normalized string representation of the URI. |
| 620 String toString(); | 619 String toString(); |
| 621 | 620 |
| 622 /** | 621 /** |
| 623 * Returns a new `Uri` based on this one, but with some parts replaced. | 622 * Returns a new `Uri` based on this one, but with some parts replaced. |
| 624 * | 623 * |
| 625 * This method takes the same parameters as the [new Uri] constructor, | 624 * This method takes the same parameters as the [new Uri] constructor, |
| 626 * and they have the same meaning. | 625 * and they have the same meaning. |
| 627 * | 626 * |
| (...skipping 24 matching lines...) Expand all Loading... |
| 652 * path: "D/E/E", | 651 * path: "D/E/E", |
| 653 * query: uri1.query, | 652 * query: uri1.query, |
| 654 * fragment: "G"); | 653 * fragment: "G"); |
| 655 * print(uri3); // prints "a://b@c:4/D/E/E?f#G" | 654 * print(uri3); // prints "a://b@c:4/D/E/E?f#G" |
| 656 * print(uri2 == uri3); // prints true. | 655 * print(uri2 == uri3); // prints true. |
| 657 * | 656 * |
| 658 * Using this method can be seen as a shorthand for the `Uri` constructor | 657 * Using this method can be seen as a shorthand for the `Uri` constructor |
| 659 * call above, but may also be slightly faster because the parts taken | 658 * call above, but may also be slightly faster because the parts taken |
| 660 * from this `Uri` need not be checked for validity again. | 659 * from this `Uri` need not be checked for validity again. |
| 661 */ | 660 */ |
| 662 Uri replace({String scheme, | 661 Uri replace( |
| 663 String userInfo, | 662 {String scheme, |
| 664 String host, | 663 String userInfo, |
| 665 int port, | 664 String host, |
| 666 String path, | 665 int port, |
| 667 Iterable<String> pathSegments, | 666 String path, |
| 668 String query, | 667 Iterable<String> pathSegments, |
| 669 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, | 668 String query, |
| 670 String fragment}); | 669 Map<String, dynamic /*String|Iterable<String>*/ > queryParameters, |
| 670 String fragment}); |
| 671 | 671 |
| 672 /** | 672 /** |
| 673 * Returns a `Uri` that differs from this only in not having a fragment. | 673 * Returns a `Uri` that differs from this only in not having a fragment. |
| 674 * | 674 * |
| 675 * If this `Uri` does not have a fragment, it is itself returned. | 675 * If this `Uri` does not have a fragment, it is itself returned. |
| 676 */ | 676 */ |
| 677 Uri removeFragment(); | 677 Uri removeFragment(); |
| 678 | 678 |
| 679 /** | 679 /** |
| 680 * Resolve [reference] as an URI relative to `this`. | 680 * Resolve [reference] as an URI relative to `this`. |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 796 return UriData._parse(uri, 5, null).uri; | 796 return UriData._parse(uri, 5, null).uri; |
| 797 } else if (dataDelta == 0x20) { | 797 } else if (dataDelta == 0x20) { |
| 798 return UriData._parse(uri.substring(start + 5, end), 0, null).uri; | 798 return UriData._parse(uri.substring(start + 5, end), 0, null).uri; |
| 799 } | 799 } |
| 800 // Otherwise the URI doesn't start with "data:" or any case variant of it. | 800 // Otherwise the URI doesn't start with "data:" or any case variant of it. |
| 801 } | 801 } |
| 802 | 802 |
| 803 // The following index-normalization belongs with the scanning, but is | 803 // The following index-normalization belongs with the scanning, but is |
| 804 // easier to do here because we already have extracted variables from the | 804 // easier to do here because we already have extracted variables from the |
| 805 // indices list. | 805 // indices list. |
| 806 var indices = new List<int>(8);//new List<int>.filled(8, start - 1); | 806 var indices = new List<int>(8); |
| 807 | 807 |
| 808 // Set default values for each position. | 808 // Set default values for each position. |
| 809 // The value will either be correct in some cases where it isn't set | 809 // The value will either be correct in some cases where it isn't set |
| 810 // by the scanner, or it is clearly recognizable as an unset value. | 810 // by the scanner, or it is clearly recognizable as an unset value. |
| 811 indices | 811 indices |
| 812 ..[0] = 0 | 812 ..[0] = 0 |
| 813 ..[_schemeEndIndex] = start - 1 | 813 ..[_schemeEndIndex] = start - 1 |
| 814 ..[_hostStartIndex] = start - 1 | 814 ..[_hostStartIndex] = start - 1 |
| 815 ..[_notSimpleIndex] = start - 1 | 815 ..[_notSimpleIndex] = start - 1 |
| 816 ..[_portStartIndex] = start | 816 ..[_portStartIndex] = start |
| (...skipping 15 matching lines...) Expand all Loading... |
| 832 indices[_notSimpleIndex] = schemeEnd; | 832 indices[_notSimpleIndex] = schemeEnd; |
| 833 } | 833 } |
| 834 } | 834 } |
| 835 // The returned positions are limited by the scanners ability to write only | 835 // The returned positions are limited by the scanners ability to write only |
| 836 // one position per character, and only the current position. | 836 // one position per character, and only the current position. |
| 837 // Scanning from left to right, we only know whether something is a scheme | 837 // Scanning from left to right, we only know whether something is a scheme |
| 838 // or a path when we see a `:` or `/`, and likewise we only know if the firs
t | 838 // or a path when we see a `:` or `/`, and likewise we only know if the firs
t |
| 839 // `/` is part of the path or is leading an authority component when we see | 839 // `/` is part of the path or is leading an authority component when we see |
| 840 // the next character. | 840 // the next character. |
| 841 | 841 |
| 842 int hostStart = indices[_hostStartIndex] + 1; | 842 int hostStart = indices[_hostStartIndex] + 1; |
| 843 int portStart = indices[_portStartIndex]; | 843 int portStart = indices[_portStartIndex]; |
| 844 int pathStart = indices[_pathStartIndex]; | 844 int pathStart = indices[_pathStartIndex]; |
| 845 int queryStart = indices[_queryStartIndex]; | 845 int queryStart = indices[_queryStartIndex]; |
| 846 int fragmentStart = indices[_fragmentStartIndex]; | 846 int fragmentStart = indices[_fragmentStartIndex]; |
| 847 | 847 |
| 848 // We may discover scheme while handling special cases. | 848 // We may discover scheme while handling special cases. |
| 849 String scheme; | 849 String scheme; |
| 850 | 850 |
| 851 // Derive some positions that weren't set to normalize the indices. | 851 // Derive some positions that weren't set to normalize the indices. |
| 852 // If pathStart isn't set (it's before scheme end or host start), then | 852 // If pathStart isn't set (it's before scheme end or host start), then |
| 853 // the path is empty. | 853 // the path is empty. |
| 854 if (fragmentStart < queryStart) queryStart = fragmentStart; | 854 if (fragmentStart < queryStart) queryStart = fragmentStart; |
| 855 if (pathStart < hostStart || pathStart <= schemeEnd) { | 855 if (pathStart < hostStart || pathStart <= schemeEnd) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 877 if (hostStart > schemeEnd + 3) { | 877 if (hostStart > schemeEnd + 3) { |
| 878 // Always be non-simple if URI contains user-info. | 878 // Always be non-simple if URI contains user-info. |
| 879 // The scanner doesn't set the not-simple position in this case because | 879 // The scanner doesn't set the not-simple position in this case because |
| 880 // it's setting the host-start position instead. | 880 // it's setting the host-start position instead. |
| 881 isSimple = false; | 881 isSimple = false; |
| 882 } else if (portStart > start && portStart + 1 == pathStart) { | 882 } else if (portStart > start && portStart + 1 == pathStart) { |
| 883 // If the port is empty, it should be omitted. | 883 // If the port is empty, it should be omitted. |
| 884 // Pathological case, don't bother correcting it. | 884 // Pathological case, don't bother correcting it. |
| 885 isSimple = false; | 885 isSimple = false; |
| 886 } else if (queryStart < end && | 886 } else if (queryStart < end && |
| 887 (queryStart == pathStart + 2 && | 887 (queryStart == pathStart + 2 && |
| 888 uri.startsWith("..", pathStart)) || | 888 uri.startsWith("..", pathStart)) || |
| 889 (queryStart > pathStart + 2 && | 889 (queryStart > pathStart + 2 && |
| 890 uri.startsWith("/..", queryStart - 3))) { | 890 uri.startsWith("/..", queryStart - 3))) { |
| 891 // The path ends in a ".." segment. This should be normalized to "../". | 891 // The path ends in a ".." segment. This should be normalized to "../". |
| 892 // We didn't detect this while scanning because a query or fragment was | 892 // We didn't detect this while scanning because a query or fragment was |
| 893 // detected at the same time (which is why we only need to check this | 893 // detected at the same time (which is why we only need to check this |
| 894 // if there is something after the path). | 894 // if there is something after the path). |
| 895 isSimple = false; | 895 isSimple = false; |
| 896 } else { | 896 } else { |
| 897 // There are a few scheme-based normalizations that | 897 // There are a few scheme-based normalizations that |
| 898 // the scanner couldn't check. | 898 // the scanner couldn't check. |
| 899 // That means that the input is very close to simple, so just do | 899 // That means that the input is very close to simple, so just do |
| 900 // the normalizations. | 900 // the normalizations. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 922 end = uri.length; | 922 end = uri.length; |
| 923 } else if (pathStart == queryStart) { | 923 } else if (pathStart == queryStart) { |
| 924 // Uri has authority and empty path. Add "/" as path. | 924 // Uri has authority and empty path. Add "/" as path. |
| 925 if (start == 0 && end == uri.length) { | 925 if (start == 0 && end == uri.length) { |
| 926 uri = uri.replaceRange(pathStart, queryStart, "/"); | 926 uri = uri.replaceRange(pathStart, queryStart, "/"); |
| 927 queryStart += 1; | 927 queryStart += 1; |
| 928 fragmentStart += 1; | 928 fragmentStart += 1; |
| 929 end += 1; | 929 end += 1; |
| 930 } else { | 930 } else { |
| 931 uri = "${uri.substring(start, pathStart)}/" | 931 uri = "${uri.substring(start, pathStart)}/" |
| 932 "${uri.substring(queryStart, end)}"; | 932 "${uri.substring(queryStart, end)}"; |
| 933 schemeEnd -= start; | 933 schemeEnd -= start; |
| 934 hostStart -= start; | 934 hostStart -= start; |
| 935 portStart -= start; | 935 portStart -= start; |
| 936 pathStart -= start; | 936 pathStart -= start; |
| 937 queryStart += 1 - start; | 937 queryStart += 1 - start; |
| 938 fragmentStart += 1 - start; | 938 fragmentStart += 1 - start; |
| 939 start = 0; | 939 start = 0; |
| 940 end = uri.length; | 940 end = uri.length; |
| 941 } | 941 } |
| 942 } | 942 } |
| 943 } else if (uri.startsWith("http", start)) { | 943 } else if (uri.startsWith("http", start)) { |
| 944 scheme = "http"; | 944 scheme = "http"; |
| 945 // HTTP URIs should not have an explicit port of 80. | 945 // HTTP URIs should not have an explicit port of 80. |
| 946 if (portStart > start && portStart + 3 == pathStart && | 946 if (portStart > start && |
| 947 portStart + 3 == pathStart && |
| 947 uri.startsWith("80", portStart + 1)) { | 948 uri.startsWith("80", portStart + 1)) { |
| 948 if (start == 0 && end == uri.length) { | 949 if (start == 0 && end == uri.length) { |
| 949 uri = uri.replaceRange(portStart, pathStart, ""); | 950 uri = uri.replaceRange(portStart, pathStart, ""); |
| 950 pathStart -= 3; | 951 pathStart -= 3; |
| 951 queryStart -= 3; | 952 queryStart -= 3; |
| 952 fragmentStart -= 3; | 953 fragmentStart -= 3; |
| 953 end -= 3; | 954 end -= 3; |
| 954 } else { | 955 } else { |
| 955 uri = uri.substring(start, portStart) + | 956 uri = uri.substring(start, portStart) + |
| 956 uri.substring(pathStart, end); | 957 uri.substring(pathStart, end); |
| 957 schemeEnd -= start; | 958 schemeEnd -= start; |
| 958 hostStart -= start; | 959 hostStart -= start; |
| 959 portStart -= start; | 960 portStart -= start; |
| 960 pathStart -= 3 + start; | 961 pathStart -= 3 + start; |
| 961 queryStart -= 3 + start; | 962 queryStart -= 3 + start; |
| 962 fragmentStart -= 3 + start; | 963 fragmentStart -= 3 + start; |
| 963 start = 0; | 964 start = 0; |
| 964 end = uri.length; | 965 end = uri.length; |
| 965 } | 966 } |
| 966 } | 967 } |
| 967 } | 968 } |
| 968 } else if (schemeEnd == start + 5 && uri.startsWith("https", start)) { | 969 } else if (schemeEnd == start + 5 && uri.startsWith("https", start)) { |
| 969 scheme = "https"; | 970 scheme = "https"; |
| 970 // HTTPS URIs should not have an explicit port of 443. | 971 // HTTPS URIs should not have an explicit port of 443. |
| 971 if (portStart > start && portStart + 4 == pathStart && | 972 if (portStart > start && |
| 973 portStart + 4 == pathStart && |
| 972 uri.startsWith("443", portStart + 1)) { | 974 uri.startsWith("443", portStart + 1)) { |
| 973 if (start == 0 && end == uri.length) { | 975 if (start == 0 && end == uri.length) { |
| 974 uri = uri.replaceRange(portStart, pathStart, ""); | 976 uri = uri.replaceRange(portStart, pathStart, ""); |
| 975 pathStart -= 4; | 977 pathStart -= 4; |
| 976 queryStart -= 4; | 978 queryStart -= 4; |
| 977 fragmentStart -= 4; | 979 fragmentStart -= 4; |
| 978 end -= 3; | 980 end -= 3; |
| 979 } else { | 981 } else { |
| 980 uri = uri.substring(start, portStart) + | 982 uri = uri.substring(start, portStart) + |
| 981 uri.substring(pathStart, end); | 983 uri.substring(pathStart, end); |
| 982 schemeEnd -= start; | 984 schemeEnd -= start; |
| 983 hostStart -= start; | 985 hostStart -= start; |
| 984 portStart -= start; | 986 portStart -= start; |
| 985 pathStart -= 4 + start; | 987 pathStart -= 4 + start; |
| 986 queryStart -= 4 + start; | 988 queryStart -= 4 + start; |
| 987 fragmentStart -= 4 + start; | 989 fragmentStart -= 4 + start; |
| 988 start = 0; | 990 start = 0; |
| 989 end = uri.length; | 991 end = uri.length; |
| 990 } | 992 } |
| 991 } | 993 } |
| 992 } | 994 } |
| 993 } | 995 } |
| 994 } | 996 } |
| 995 | 997 |
| 996 if (isSimple) { | 998 if (isSimple) { |
| 997 if (start > 0 || end < uri.length) { | 999 if (start > 0 || end < uri.length) { |
| 998 uri = uri.substring(start, end); | 1000 uri = uri.substring(start, end); |
| 999 schemeEnd -= start; | 1001 schemeEnd -= start; |
| 1000 hostStart -= start; | 1002 hostStart -= start; |
| 1001 portStart -= start; | 1003 portStart -= start; |
| 1002 pathStart -= start; | 1004 pathStart -= start; |
| 1003 queryStart -= start; | 1005 queryStart -= start; |
| 1004 fragmentStart -= start; | 1006 fragmentStart -= start; |
| 1005 } | 1007 } |
| 1006 return new _SimpleUri(uri, schemeEnd, hostStart, portStart, pathStart, | 1008 return new _SimpleUri(uri, schemeEnd, hostStart, portStart, pathStart, |
| 1007 queryStart, fragmentStart, scheme); | 1009 queryStart, fragmentStart, scheme); |
| 1008 | |
| 1009 } | 1010 } |
| 1010 | 1011 |
| 1011 return new _Uri.notSimple(uri, start, end, schemeEnd, hostStart, portStart, | 1012 return new _Uri.notSimple(uri, start, end, schemeEnd, hostStart, portStart, |
| 1012 pathStart, queryStart, fragmentStart, scheme); | 1013 pathStart, queryStart, fragmentStart, scheme); |
| 1013 } | 1014 } |
| 1014 | 1015 |
| 1015 /** | 1016 /** |
| 1016 * Encode the string [component] using percent-encoding to make it | 1017 * Encode the string [component] using percent-encoding to make it |
| 1017 * safe for literal use as a URI component. | 1018 * safe for literal use as a URI component. |
| 1018 * | 1019 * |
| 1019 * All characters except uppercase and lowercase letters, digits and | 1020 * All characters except uppercase and lowercase letters, digits and |
| 1020 * the characters `-_.!~*'()` are percent-encoded. This is the | 1021 * the characters `-_.!~*'()` are percent-encoded. This is the |
| 1021 * set of characters specified in RFC 2396 and the which is | 1022 * set of characters specified in RFC 2396 and the which is |
| 1022 * specified for the encodeUriComponent in ECMA-262 version 5.1. | 1023 * specified for the encodeUriComponent in ECMA-262 version 5.1. |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1063 * part separately before building the query string. | 1064 * part separately before building the query string. |
| 1064 * | 1065 * |
| 1065 * To avoid the need for explicitly encoding the query use the | 1066 * To avoid the need for explicitly encoding the query use the |
| 1066 * [queryParameters] optional named arguments when constructing a | 1067 * [queryParameters] optional named arguments when constructing a |
| 1067 * [Uri]. | 1068 * [Uri]. |
| 1068 * | 1069 * |
| 1069 * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more | 1070 * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more |
| 1070 * details. | 1071 * details. |
| 1071 */ | 1072 */ |
| 1072 static String encodeQueryComponent(String component, | 1073 static String encodeQueryComponent(String component, |
| 1073 {Encoding encoding: UTF8}) { | 1074 {Encoding encoding: UTF8}) { |
| 1074 return _Uri._uriEncode(_Uri._unreservedTable, component, encoding, true); | 1075 return _Uri._uriEncode(_Uri._unreservedTable, component, encoding, true); |
| 1075 } | 1076 } |
| 1076 | 1077 |
| 1077 /** | 1078 /** |
| 1078 * Decodes the percent-encoding in [encodedComponent]. | 1079 * Decodes the percent-encoding in [encodedComponent]. |
| 1079 * | 1080 * |
| 1080 * Note that decoding a URI component might change its meaning as | 1081 * Note that decoding a URI component might change its meaning as |
| 1081 * some of the decoded characters could be characters with are | 1082 * some of the decoded characters could be characters with are |
| 1082 * delimiters for a given URI component type. Always split a URI | 1083 * delimiters for a given URI component type. Always split a URI |
| 1083 * component using the delimiters for the component before decoding | 1084 * component using the delimiters for the component before decoding |
| 1084 * the individual parts. | 1085 * the individual parts. |
| 1085 * | 1086 * |
| 1086 * For handling the [path] and [query] components consider using | 1087 * For handling the [path] and [query] components consider using |
| 1087 * [pathSegments] and [queryParameters] to get the separated and | 1088 * [pathSegments] and [queryParameters] to get the separated and |
| 1088 * decoded component. | 1089 * decoded component. |
| 1089 */ | 1090 */ |
| 1090 static String decodeComponent(String encodedComponent) { | 1091 static String decodeComponent(String encodedComponent) { |
| 1091 return _Uri._uriDecode(encodedComponent, 0, encodedComponent.length, | 1092 return _Uri._uriDecode( |
| 1092 UTF8, false); | 1093 encodedComponent, 0, encodedComponent.length, UTF8, false); |
| 1093 } | 1094 } |
| 1094 | 1095 |
| 1095 /** | 1096 /** |
| 1096 * Decodes the percent-encoding in [encodedComponent], converting | 1097 * Decodes the percent-encoding in [encodedComponent], converting |
| 1097 * pluses to spaces. | 1098 * pluses to spaces. |
| 1098 * | 1099 * |
| 1099 * It will create a byte-list of the decoded characters, and then use | 1100 * It will create a byte-list of the decoded characters, and then use |
| 1100 * [encoding] to decode the byte-list to a String. The default encoding is | 1101 * [encoding] to decode the byte-list to a String. The default encoding is |
| 1101 * UTF-8. | 1102 * UTF-8. |
| 1102 */ | 1103 */ |
| 1103 static String decodeQueryComponent( | 1104 static String decodeQueryComponent(String encodedComponent, |
| 1104 String encodedComponent, | |
| 1105 {Encoding encoding: UTF8}) { | 1105 {Encoding encoding: UTF8}) { |
| 1106 return _Uri._uriDecode(encodedComponent, 0, encodedComponent.length, | 1106 return _Uri._uriDecode( |
| 1107 encoding, true); | 1107 encodedComponent, 0, encodedComponent.length, encoding, true); |
| 1108 } | 1108 } |
| 1109 | 1109 |
| 1110 /** | 1110 /** |
| 1111 * Encode the string [uri] using percent-encoding to make it | 1111 * Encode the string [uri] using percent-encoding to make it |
| 1112 * safe for literal use as a full URI. | 1112 * safe for literal use as a full URI. |
| 1113 * | 1113 * |
| 1114 * All characters except uppercase and lowercase letters, digits and | 1114 * All characters except uppercase and lowercase letters, digits and |
| 1115 * the characters `!#$&'()*+,-./:;=?@_~` are percent-encoded. This | 1115 * the characters `!#$&'()*+,-./:;=?@_~` are percent-encoded. This |
| 1116 * is the set of characters specified in in ECMA-262 version 5.1 for | 1116 * is the set of characters specified in in ECMA-262 version 5.1 for |
| 1117 * the encodeURI function . | 1117 * the encodeURI function . |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1139 * Each key and value in the returned map has been decoded. If the [query] | 1139 * Each key and value in the returned map has been decoded. If the [query] |
| 1140 * is the empty string an empty map is returned. | 1140 * is the empty string an empty map is returned. |
| 1141 * | 1141 * |
| 1142 * Keys in the query string that have no value are mapped to the | 1142 * Keys in the query string that have no value are mapped to the |
| 1143 * empty string. | 1143 * empty string. |
| 1144 * | 1144 * |
| 1145 * Each query component will be decoded using [encoding]. The default encoding | 1145 * Each query component will be decoded using [encoding]. The default encoding |
| 1146 * is UTF-8. | 1146 * is UTF-8. |
| 1147 */ | 1147 */ |
| 1148 static Map<String, String> splitQueryString(String query, | 1148 static Map<String, String> splitQueryString(String query, |
| 1149 {Encoding encoding: UTF8}) { | 1149 {Encoding encoding: UTF8}) { |
| 1150 return query.split("&").fold({}, (map, element) { | 1150 return query.split("&").fold({}, (map, element) { |
| 1151 int index = element.indexOf("="); | 1151 int index = element.indexOf("="); |
| 1152 if (index == -1) { | 1152 if (index == -1) { |
| 1153 if (element != "") { | 1153 if (element != "") { |
| 1154 map[decodeQueryComponent(element, encoding: encoding)] = ""; | 1154 map[decodeQueryComponent(element, encoding: encoding)] = ""; |
| 1155 } | 1155 } |
| 1156 } else if (index != 0) { | 1156 } else if (index != 0) { |
| 1157 var key = element.substring(0, index); | 1157 var key = element.substring(0, index); |
| 1158 var value = element.substring(index + 1); | 1158 var value = element.substring(index + 1); |
| 1159 map[decodeQueryComponent(key, encoding: encoding)] = | 1159 map[decodeQueryComponent(key, encoding: encoding)] = |
| 1160 decodeQueryComponent(value, encoding: encoding); | 1160 decodeQueryComponent(value, encoding: encoding); |
| 1161 } | 1161 } |
| 1162 return map; | 1162 return map; |
| 1163 }); | 1163 }); |
| 1164 } | 1164 } |
| 1165 | 1165 |
| 1166 | |
| 1167 /** | 1166 /** |
| 1168 * Parse the [host] as an IP version 4 (IPv4) address, returning the address | 1167 * Parse the [host] as an IP version 4 (IPv4) address, returning the address |
| 1169 * as a list of 4 bytes in network byte order (big endian). | 1168 * as a list of 4 bytes in network byte order (big endian). |
| 1170 * | 1169 * |
| 1171 * Throws a [FormatException] if [host] is not a valid IPv4 address | 1170 * Throws a [FormatException] if [host] is not a valid IPv4 address |
| 1172 * representation. | 1171 * representation. |
| 1173 */ | 1172 */ |
| 1174 static List<int> parseIPv4Address(String host) => | 1173 static List<int> parseIPv4Address(String host) => |
| 1175 _parseIPv4Address(host, 0, host.length); | 1174 _parseIPv4Address(host, 0, host.length); |
| 1176 | 1175 |
| 1177 /// Implementation of [parseIPv4Address] that can work on a substring. | 1176 /// Implementation of [parseIPv4Address] that can work on a substring. |
| 1178 static List<int> _parseIPv4Address(String host, int start, int end) { | 1177 static List<int> _parseIPv4Address(String host, int start, int end) { |
| 1179 void error(String msg, int position) { | 1178 void error(String msg, int position) { |
| 1180 throw new FormatException('Illegal IPv4 address, $msg', host, position); | 1179 throw new FormatException('Illegal IPv4 address, $msg', host, position); |
| 1181 } | 1180 } |
| 1182 | 1181 |
| 1183 var result = new Uint8List(4); | 1182 var result = new Uint8List(4); |
| 1184 int partIndex = 0; | 1183 int partIndex = 0; |
| 1185 int partStart = start; | 1184 int partStart = start; |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1406 /// | 1405 /// |
| 1407 /// The components must be properly normalized. | 1406 /// The components must be properly normalized. |
| 1408 /// | 1407 /// |
| 1409 /// Use `null` for [_host] if there is no authority. In that case, always | 1408 /// Use `null` for [_host] if there is no authority. In that case, always |
| 1410 /// pass `null` for [_port] and [_userInfo] as well. | 1409 /// pass `null` for [_port] and [_userInfo] as well. |
| 1411 /// | 1410 /// |
| 1412 /// Use `null` for [_port], [_userInfo], [_query] and [_fragment] if there is | 1411 /// Use `null` for [_port], [_userInfo], [_query] and [_fragment] if there is |
| 1413 /// component of that type. | 1412 /// component of that type. |
| 1414 /// | 1413 /// |
| 1415 /// The [path] and [scheme] are never empty. | 1414 /// The [path] and [scheme] are never empty. |
| 1416 _Uri._internal(this.scheme, | 1415 _Uri._internal(this.scheme, this._userInfo, this._host, this._port, this.path, |
| 1417 this._userInfo, | 1416 this._query, this._fragment); |
| 1418 this._host, | |
| 1419 this._port, | |
| 1420 this.path, | |
| 1421 this._query, | |
| 1422 this._fragment); | |
| 1423 | 1417 |
| 1424 /// Create a [_Uri] from parts of [uri]. | 1418 /// Create a [_Uri] from parts of [uri]. |
| 1425 /// | 1419 /// |
| 1426 /// The parameters specify the start/end of particular components of the URI. | 1420 /// The parameters specify the start/end of particular components of the URI. |
| 1427 /// The [scheme] may contain a string representing a normalized scheme | 1421 /// The [scheme] may contain a string representing a normalized scheme |
| 1428 /// component if one has already been discovered. | 1422 /// component if one has already been discovered. |
| 1429 factory _Uri.notSimple(String uri, int start, int end, int schemeEnd, | 1423 factory _Uri.notSimple( |
| 1430 int hostStart, int portStart, int pathStart, | 1424 String uri, |
| 1431 int queryStart, int fragmentStart, String scheme) { | 1425 int start, |
| 1426 int end, |
| 1427 int schemeEnd, |
| 1428 int hostStart, |
| 1429 int portStart, |
| 1430 int pathStart, |
| 1431 int queryStart, |
| 1432 int fragmentStart, |
| 1433 String scheme) { |
| 1432 if (scheme == null) { | 1434 if (scheme == null) { |
| 1433 scheme = ""; | 1435 scheme = ""; |
| 1434 if (schemeEnd > start) { | 1436 if (schemeEnd > start) { |
| 1435 scheme = _makeScheme(uri, start, schemeEnd); | 1437 scheme = _makeScheme(uri, start, schemeEnd); |
| 1436 } else if (schemeEnd == start) { | 1438 } else if (schemeEnd == start) { |
| 1437 _fail(uri, start, "Invalid empty scheme"); | 1439 _fail(uri, start, "Invalid empty scheme"); |
| 1438 } | 1440 } |
| 1439 } | 1441 } |
| 1440 String userInfo = ""; | 1442 String userInfo = ""; |
| 1441 String host; | 1443 String host; |
| 1442 int port; | 1444 int port; |
| 1443 if (hostStart > start) { | 1445 if (hostStart > start) { |
| 1444 int userInfoStart = schemeEnd + 3; | 1446 int userInfoStart = schemeEnd + 3; |
| 1445 if (userInfoStart < hostStart) { | 1447 if (userInfoStart < hostStart) { |
| 1446 userInfo = _makeUserInfo(uri, userInfoStart, hostStart - 1); | 1448 userInfo = _makeUserInfo(uri, userInfoStart, hostStart - 1); |
| 1447 } | 1449 } |
| 1448 host = _makeHost(uri, hostStart, portStart, false); | 1450 host = _makeHost(uri, hostStart, portStart, false); |
| 1449 if (portStart + 1 < pathStart) { | 1451 if (portStart + 1 < pathStart) { |
| 1450 // Should throw because invalid. | 1452 // Should throw because invalid. |
| 1451 port = int.parse(uri.substring(portStart + 1, pathStart), onError: (_) { | 1453 port = int.parse(uri.substring(portStart + 1, pathStart), onError: (_) { |
| 1452 throw new FormatException("Invalid port", uri, portStart + 1); | 1454 throw new FormatException("Invalid port", uri, portStart + 1); |
| 1453 }); | 1455 }); |
| 1454 port = _makePort(port, scheme); | 1456 port = _makePort(port, scheme); |
| 1455 } | 1457 } |
| 1456 } | 1458 } |
| 1457 String path = _makePath(uri, pathStart, queryStart, null, | 1459 String path = |
| 1458 scheme, host != null); | 1460 _makePath(uri, pathStart, queryStart, null, scheme, host != null); |
| 1459 String query; | 1461 String query; |
| 1460 if (queryStart < fragmentStart) { | 1462 if (queryStart < fragmentStart) { |
| 1461 query = _makeQuery(uri, queryStart + 1, fragmentStart, null); | 1463 query = _makeQuery(uri, queryStart + 1, fragmentStart, null); |
| 1462 } | 1464 } |
| 1463 String fragment; | 1465 String fragment; |
| 1464 if (fragmentStart < end) { | 1466 if (fragmentStart < end) { |
| 1465 fragment = _makeFragment(uri, fragmentStart + 1, end); | 1467 fragment = _makeFragment(uri, fragmentStart + 1, end); |
| 1466 } | 1468 } |
| 1467 return new _Uri._internal(scheme, | 1469 return new _Uri._internal( |
| 1468 userInfo, | 1470 scheme, userInfo, host, port, path, query, fragment); |
| 1469 host, | |
| 1470 port, | |
| 1471 path, | |
| 1472 query, | |
| 1473 fragment); | |
| 1474 } | 1471 } |
| 1475 | 1472 |
| 1476 /// Implementation of [Uri.Uri]. | 1473 /// Implementation of [Uri.Uri]. |
| 1477 factory _Uri({String scheme, | 1474 factory _Uri( |
| 1478 String userInfo, | 1475 {String scheme, |
| 1479 String host, | 1476 String userInfo, |
| 1480 int port, | 1477 String host, |
| 1481 String path, | 1478 int port, |
| 1482 Iterable<String> pathSegments, | 1479 String path, |
| 1483 String query, | 1480 Iterable<String> pathSegments, |
| 1484 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, | 1481 String query, |
| 1485 String fragment}) { | 1482 Map<String, dynamic /*String|Iterable<String>*/ > queryParameters, |
| 1483 String fragment}) { |
| 1486 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); | 1484 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); |
| 1487 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); | 1485 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); |
| 1488 host = _makeHost(host, 0, _stringOrNullLength(host), false); | 1486 host = _makeHost(host, 0, _stringOrNullLength(host), false); |
| 1489 // Special case this constructor for backwards compatibility. | 1487 // Special case this constructor for backwards compatibility. |
| 1490 if (query == "") query = null; | 1488 if (query == "") query = null; |
| 1491 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); | 1489 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); |
| 1492 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); | 1490 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); |
| 1493 port = _makePort(port, scheme); | 1491 port = _makePort(port, scheme); |
| 1494 bool isFile = (scheme == "file"); | 1492 bool isFile = (scheme == "file"); |
| 1495 if (host == null && | 1493 if (host == null && (userInfo.isNotEmpty || port != null || isFile)) { |
| 1496 (userInfo.isNotEmpty || port != null || isFile)) { | |
| 1497 host = ""; | 1494 host = ""; |
| 1498 } | 1495 } |
| 1499 bool hasAuthority = (host != null); | 1496 bool hasAuthority = (host != null); |
| 1500 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, | 1497 path = _makePath( |
| 1501 scheme, hasAuthority); | 1498 path, 0, _stringOrNullLength(path), pathSegments, scheme, hasAuthority); |
| 1502 if (scheme.isEmpty && host == null && !path.startsWith('/')) { | 1499 if (scheme.isEmpty && host == null && !path.startsWith('/')) { |
| 1503 bool allowScheme = scheme.isNotEmpty || host != null; | 1500 bool allowScheme = scheme.isNotEmpty || host != null; |
| 1504 path = _normalizeRelativePath(path, allowScheme); | 1501 path = _normalizeRelativePath(path, allowScheme); |
| 1505 } else { | 1502 } else { |
| 1506 path = _removeDotSegments(path); | 1503 path = _removeDotSegments(path); |
| 1507 } | 1504 } |
| 1508 if (host == null && path.startsWith("//")) { | 1505 if (host == null && path.startsWith("//")) { |
| 1509 host = ""; | 1506 host = ""; |
| 1510 } | 1507 } |
| 1511 return new _Uri._internal(scheme, userInfo, host, port, | 1508 return new _Uri._internal( |
| 1512 path, query, fragment); | 1509 scheme, userInfo, host, port, path, query, fragment); |
| 1513 } | 1510 } |
| 1514 | 1511 |
| 1515 /// Implementation of [Uri.http]. | 1512 /// Implementation of [Uri.http]. |
| 1516 factory _Uri.http(String authority, | 1513 factory _Uri.http(String authority, String unencodedPath, |
| 1517 String unencodedPath, | 1514 [Map<String, String> queryParameters]) { |
| 1518 [Map<String, String> queryParameters]) { | |
| 1519 return _makeHttpUri("http", authority, unencodedPath, queryParameters); | 1515 return _makeHttpUri("http", authority, unencodedPath, queryParameters); |
| 1520 } | 1516 } |
| 1521 | 1517 |
| 1522 /// Implementation of [Uri.https]. | 1518 /// Implementation of [Uri.https]. |
| 1523 factory _Uri.https(String authority, | 1519 factory _Uri.https(String authority, String unencodedPath, |
| 1524 String unencodedPath, | 1520 [Map<String, String> queryParameters]) { |
| 1525 [Map<String, String> queryParameters]) { | |
| 1526 return _makeHttpUri("https", authority, unencodedPath, queryParameters); | 1521 return _makeHttpUri("https", authority, unencodedPath, queryParameters); |
| 1527 } | 1522 } |
| 1528 | 1523 |
| 1529 String get authority { | 1524 String get authority { |
| 1530 if (!hasAuthority) return ""; | 1525 if (!hasAuthority) return ""; |
| 1531 var sb = new StringBuffer(); | 1526 var sb = new StringBuffer(); |
| 1532 _writeAuthority(sb); | 1527 _writeAuthority(sb); |
| 1533 return sb.toString(); | 1528 return sb.toString(); |
| 1534 } | 1529 } |
| 1535 | 1530 |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1595 } | 1590 } |
| 1596 } | 1591 } |
| 1597 return true; | 1592 return true; |
| 1598 } | 1593 } |
| 1599 | 1594 |
| 1600 // Report a parse failure. | 1595 // Report a parse failure. |
| 1601 static void _fail(String uri, int index, String message) { | 1596 static void _fail(String uri, int index, String message) { |
| 1602 throw new FormatException(message, uri, index); | 1597 throw new FormatException(message, uri, index); |
| 1603 } | 1598 } |
| 1604 | 1599 |
| 1605 static Uri _makeHttpUri(String scheme, | 1600 static Uri _makeHttpUri(String scheme, String authority, String unencodedPath, |
| 1606 String authority, | 1601 Map<String, String> queryParameters) { |
| 1607 String unencodedPath, | |
| 1608 Map<String, String> queryParameters) { | |
| 1609 var userInfo = ""; | 1602 var userInfo = ""; |
| 1610 var host = null; | 1603 var host = null; |
| 1611 var port = null; | 1604 var port = null; |
| 1612 | 1605 |
| 1613 if (authority != null && authority.isNotEmpty) { | 1606 if (authority != null && authority.isNotEmpty) { |
| 1614 var hostStart = 0; | 1607 var hostStart = 0; |
| 1615 // Split off the user info. | 1608 // Split off the user info. |
| 1616 bool hasUserInfo = false; | 1609 bool hasUserInfo = false; |
| 1617 for (int i = 0; i < authority.length; i++) { | 1610 for (int i = 0; i < authority.length; i++) { |
| 1618 const int atSign = 0x40; | 1611 const int atSign = 0x40; |
| 1619 if (authority.codeUnitAt(i) == atSign) { | 1612 if (authority.codeUnitAt(i) == atSign) { |
| 1620 hasUserInfo = true; | 1613 hasUserInfo = true; |
| 1621 userInfo = authority.substring(0, i); | 1614 userInfo = authority.substring(0, i); |
| 1622 hostStart = i + 1; | 1615 hostStart = i + 1; |
| 1623 break; | 1616 break; |
| 1624 } | 1617 } |
| 1625 } | 1618 } |
| 1626 var hostEnd = hostStart; | 1619 var hostEnd = hostStart; |
| 1627 if (hostStart < authority.length && | 1620 if (hostStart < authority.length && |
| 1628 authority.codeUnitAt(hostStart) == _LEFT_BRACKET) { | 1621 authority.codeUnitAt(hostStart) == _LEFT_BRACKET) { |
| 1629 // IPv6 host. | 1622 // IPv6 host. |
| 1630 for (; hostEnd < authority.length; hostEnd++) { | 1623 for (; hostEnd < authority.length; hostEnd++) { |
| 1631 if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break; | 1624 if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break; |
| 1632 } | 1625 } |
| 1633 if (hostEnd == authority.length) { | 1626 if (hostEnd == authority.length) { |
| 1634 throw new FormatException("Invalid IPv6 host entry.", | 1627 throw new FormatException( |
| 1635 authority, hostStart); | 1628 "Invalid IPv6 host entry.", authority, hostStart); |
| 1636 } | 1629 } |
| 1637 Uri.parseIPv6Address(authority, hostStart + 1, hostEnd); | 1630 Uri.parseIPv6Address(authority, hostStart + 1, hostEnd); |
| 1638 hostEnd++; // Skip the closing bracket. | 1631 hostEnd++; // Skip the closing bracket. |
| 1639 if (hostEnd != authority.length && | 1632 if (hostEnd != authority.length && |
| 1640 authority.codeUnitAt(hostEnd) != _COLON) { | 1633 authority.codeUnitAt(hostEnd) != _COLON) { |
| 1641 throw new FormatException("Invalid end of authority", | 1634 throw new FormatException( |
| 1642 authority, hostEnd); | 1635 "Invalid end of authority", authority, hostEnd); |
| 1643 } | 1636 } |
| 1644 } | 1637 } |
| 1645 // Split host and port. | 1638 // Split host and port. |
| 1646 bool hasPort = false; | 1639 bool hasPort = false; |
| 1647 for (; hostEnd < authority.length; hostEnd++) { | 1640 for (; hostEnd < authority.length; hostEnd++) { |
| 1648 if (authority.codeUnitAt(hostEnd) == _COLON) { | 1641 if (authority.codeUnitAt(hostEnd) == _COLON) { |
| 1649 var portString = authority.substring(hostEnd + 1); | 1642 var portString = authority.substring(hostEnd + 1); |
| 1650 // We allow the empty port - falling back to initial value. | 1643 // We allow the empty port - falling back to initial value. |
| 1651 if (portString.isNotEmpty) port = int.parse(portString); | 1644 if (portString.isNotEmpty) port = int.parse(portString); |
| 1652 break; | 1645 break; |
| 1653 } | 1646 } |
| 1654 } | 1647 } |
| 1655 host = authority.substring(hostStart, hostEnd); | 1648 host = authority.substring(hostStart, hostEnd); |
| 1656 } | 1649 } |
| 1657 return new Uri(scheme: scheme, | 1650 return new Uri( |
| 1658 userInfo: userInfo, | 1651 scheme: scheme, |
| 1659 host: host, | 1652 userInfo: userInfo, |
| 1660 port: port, | 1653 host: host, |
| 1661 pathSegments: unencodedPath.split("/"), | 1654 port: port, |
| 1662 queryParameters: queryParameters); | 1655 pathSegments: unencodedPath.split("/"), |
| 1656 queryParameters: queryParameters); |
| 1663 } | 1657 } |
| 1664 | 1658 |
| 1665 /// Implementation of [Uri.file]. | 1659 /// Implementation of [Uri.file]. |
| 1666 factory _Uri.file(String path, {bool windows}) { | 1660 factory _Uri.file(String path, {bool windows}) { |
| 1667 windows = (windows == null) ? _Uri._isWindows : windows; | 1661 windows = (windows == null) ? _Uri._isWindows : windows; |
| 1668 return windows ? _makeWindowsFileUrl(path, false) | 1662 return windows |
| 1669 : _makeFileUri(path, false); | 1663 ? _makeWindowsFileUrl(path, false) |
| 1664 : _makeFileUri(path, false); |
| 1670 } | 1665 } |
| 1671 | 1666 |
| 1672 /// Implementation of [Uri.directory]. | 1667 /// Implementation of [Uri.directory]. |
| 1673 factory _Uri.directory(String path, {bool windows}) { | 1668 factory _Uri.directory(String path, {bool windows}) { |
| 1674 windows = (windows == null) ? _Uri._isWindows : windows; | 1669 windows = (windows == null) ? _Uri._isWindows : windows; |
| 1675 return windows ? _makeWindowsFileUrl(path, true) | 1670 return windows ? _makeWindowsFileUrl(path, true) : _makeFileUri(path, true); |
| 1676 : _makeFileUri(path, true); | |
| 1677 } | 1671 } |
| 1678 | 1672 |
| 1679 | |
| 1680 /// Used internally in path-related constructors. | 1673 /// Used internally in path-related constructors. |
| 1681 external static bool get _isWindows; | 1674 external static bool get _isWindows; |
| 1682 | 1675 |
| 1683 static _checkNonWindowsPathReservedCharacters(List<String> segments, | 1676 static _checkNonWindowsPathReservedCharacters( |
| 1684 bool argumentError) { | 1677 List<String> segments, bool argumentError) { |
| 1685 segments.forEach((segment) { | 1678 segments.forEach((segment) { |
| 1686 if (segment.contains("/")) { | 1679 if (segment.contains("/")) { |
| 1687 if (argumentError) { | 1680 if (argumentError) { |
| 1688 throw new ArgumentError("Illegal path character $segment"); | 1681 throw new ArgumentError("Illegal path character $segment"); |
| 1689 } else { | 1682 } else { |
| 1690 throw new UnsupportedError("Illegal path character $segment"); | 1683 throw new UnsupportedError("Illegal path character $segment"); |
| 1691 } | 1684 } |
| 1692 } | 1685 } |
| 1693 }); | 1686 }); |
| 1694 } | 1687 } |
| 1695 | 1688 |
| 1696 static _checkWindowsPathReservedCharacters(List<String> segments, | 1689 static _checkWindowsPathReservedCharacters( |
| 1697 bool argumentError, | 1690 List<String> segments, bool argumentError, |
| 1698 [int firstSegment = 0]) { | 1691 [int firstSegment = 0]) { |
| 1699 for (var segment in segments.skip(firstSegment)) { | 1692 for (var segment in segments.skip(firstSegment)) { |
| 1700 if (segment.contains(new RegExp(r'["*/:<>?\\|]'))) { | 1693 if (segment.contains(new RegExp(r'["*/:<>?\\|]'))) { |
| 1701 if (argumentError) { | 1694 if (argumentError) { |
| 1702 throw new ArgumentError("Illegal character in path"); | 1695 throw new ArgumentError("Illegal character in path"); |
| 1703 } else { | 1696 } else { |
| 1704 throw new UnsupportedError("Illegal character in path"); | 1697 throw new UnsupportedError("Illegal character in path"); |
| 1705 } | 1698 } |
| 1706 } | 1699 } |
| 1707 } | 1700 } |
| 1708 } | 1701 } |
| 1709 | 1702 |
| 1710 static _checkWindowsDriveLetter(int charCode, bool argumentError) { | 1703 static _checkWindowsDriveLetter(int charCode, bool argumentError) { |
| 1711 if ((_UPPER_CASE_A <= charCode && charCode <= _UPPER_CASE_Z) || | 1704 if ((_UPPER_CASE_A <= charCode && charCode <= _UPPER_CASE_Z) || |
| 1712 (_LOWER_CASE_A <= charCode && charCode <= _LOWER_CASE_Z)) { | 1705 (_LOWER_CASE_A <= charCode && charCode <= _LOWER_CASE_Z)) { |
| 1713 return; | 1706 return; |
| 1714 } | 1707 } |
| 1715 if (argumentError) { | 1708 if (argumentError) { |
| 1716 throw new ArgumentError("Illegal drive letter " + | 1709 throw new ArgumentError( |
| 1717 new String.fromCharCode(charCode)); | 1710 "Illegal drive letter " + new String.fromCharCode(charCode)); |
| 1718 } else { | 1711 } else { |
| 1719 throw new UnsupportedError("Illegal drive letter " + | 1712 throw new UnsupportedError( |
| 1720 new String.fromCharCode(charCode)); | 1713 "Illegal drive letter " + new String.fromCharCode(charCode)); |
| 1721 } | 1714 } |
| 1722 } | 1715 } |
| 1723 | 1716 |
| 1724 static _makeFileUri(String path, bool slashTerminated) { | 1717 static _makeFileUri(String path, bool slashTerminated) { |
| 1725 const String sep = "/"; | 1718 const String sep = "/"; |
| 1726 var segments = path.split(sep); | 1719 var segments = path.split(sep); |
| 1727 if (slashTerminated && | 1720 if (slashTerminated && segments.isNotEmpty && segments.last.isNotEmpty) { |
| 1728 segments.isNotEmpty && | 1721 segments.add(""); // Extra separator at end. |
| 1729 segments.last.isNotEmpty) { | |
| 1730 segments.add(""); // Extra separator at end. | |
| 1731 } | 1722 } |
| 1732 if (path.startsWith(sep)) { | 1723 if (path.startsWith(sep)) { |
| 1733 // Absolute file:// URI. | 1724 // Absolute file:// URI. |
| 1734 return new Uri(scheme: "file", pathSegments: segments); | 1725 return new Uri(scheme: "file", pathSegments: segments); |
| 1735 } else { | 1726 } else { |
| 1736 // Relative URI. | 1727 // Relative URI. |
| 1737 return new Uri(pathSegments: segments); | 1728 return new Uri(pathSegments: segments); |
| 1738 } | 1729 } |
| 1739 } | 1730 } |
| 1740 | 1731 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1756 } | 1747 } |
| 1757 const String sep = r'\'; | 1748 const String sep = r'\'; |
| 1758 if (path.length > 1 && path.codeUnitAt(1) == _COLON) { | 1749 if (path.length > 1 && path.codeUnitAt(1) == _COLON) { |
| 1759 _checkWindowsDriveLetter(path.codeUnitAt(0), true); | 1750 _checkWindowsDriveLetter(path.codeUnitAt(0), true); |
| 1760 if (path.length == 2 || path.codeUnitAt(2) != _BACKSLASH) { | 1751 if (path.length == 2 || path.codeUnitAt(2) != _BACKSLASH) { |
| 1761 throw new ArgumentError( | 1752 throw new ArgumentError( |
| 1762 "Windows paths with drive letter must be absolute"); | 1753 "Windows paths with drive letter must be absolute"); |
| 1763 } | 1754 } |
| 1764 // Absolute file://C:/ URI. | 1755 // Absolute file://C:/ URI. |
| 1765 var pathSegments = path.split(sep); | 1756 var pathSegments = path.split(sep); |
| 1766 if (slashTerminated && | 1757 if (slashTerminated && pathSegments.last.isNotEmpty) { |
| 1767 pathSegments.last.isNotEmpty) { | 1758 pathSegments.add(""); // Extra separator at end. |
| 1768 pathSegments.add(""); // Extra separator at end. | |
| 1769 } | 1759 } |
| 1770 _checkWindowsPathReservedCharacters(pathSegments, true, 1); | 1760 _checkWindowsPathReservedCharacters(pathSegments, true, 1); |
| 1771 return new Uri(scheme: "file", pathSegments: pathSegments); | 1761 return new Uri(scheme: "file", pathSegments: pathSegments); |
| 1772 } | 1762 } |
| 1773 | 1763 |
| 1774 if (path.startsWith(sep)) { | 1764 if (path.startsWith(sep)) { |
| 1775 if (path.startsWith(sep, 1)) { | 1765 if (path.startsWith(sep, 1)) { |
| 1776 // Absolute file:// URI with host. | 1766 // Absolute file:// URI with host. |
| 1777 int pathStart = path.indexOf(r'\', 2); | 1767 int pathStart = path.indexOf(r'\', 2); |
| 1778 String hostPart = | 1768 String hostPart = |
| 1779 (pathStart < 0) ? path.substring(2) : path.substring(2, pathStart); | 1769 (pathStart < 0) ? path.substring(2) : path.substring(2, pathStart); |
| 1780 String pathPart = | 1770 String pathPart = (pathStart < 0) ? "" : path.substring(pathStart + 1); |
| 1781 (pathStart < 0) ? "" : path.substring(pathStart + 1); | |
| 1782 var pathSegments = pathPart.split(sep); | 1771 var pathSegments = pathPart.split(sep); |
| 1783 _checkWindowsPathReservedCharacters(pathSegments, true); | 1772 _checkWindowsPathReservedCharacters(pathSegments, true); |
| 1784 if (slashTerminated && | 1773 if (slashTerminated && pathSegments.last.isNotEmpty) { |
| 1785 pathSegments.last.isNotEmpty) { | 1774 pathSegments.add(""); // Extra separator at end. |
| 1786 pathSegments.add(""); // Extra separator at end. | |
| 1787 } | 1775 } |
| 1788 return new Uri( | 1776 return new Uri( |
| 1789 scheme: "file", host: hostPart, pathSegments: pathSegments); | 1777 scheme: "file", host: hostPart, pathSegments: pathSegments); |
| 1790 } else { | 1778 } else { |
| 1791 // Absolute file:// URI. | 1779 // Absolute file:// URI. |
| 1792 var pathSegments = path.split(sep); | 1780 var pathSegments = path.split(sep); |
| 1793 if (slashTerminated && | 1781 if (slashTerminated && pathSegments.last.isNotEmpty) { |
| 1794 pathSegments.last.isNotEmpty) { | 1782 pathSegments.add(""); // Extra separator at end. |
| 1795 pathSegments.add(""); // Extra separator at end. | |
| 1796 } | 1783 } |
| 1797 _checkWindowsPathReservedCharacters(pathSegments, true); | 1784 _checkWindowsPathReservedCharacters(pathSegments, true); |
| 1798 return new Uri(scheme: "file", pathSegments: pathSegments); | 1785 return new Uri(scheme: "file", pathSegments: pathSegments); |
| 1799 } | 1786 } |
| 1800 } else { | 1787 } else { |
| 1801 // Relative URI. | 1788 // Relative URI. |
| 1802 var pathSegments = path.split(sep); | 1789 var pathSegments = path.split(sep); |
| 1803 _checkWindowsPathReservedCharacters(pathSegments, true); | 1790 _checkWindowsPathReservedCharacters(pathSegments, true); |
| 1804 if (slashTerminated && | 1791 if (slashTerminated && |
| 1805 pathSegments.isNotEmpty && | 1792 pathSegments.isNotEmpty && |
| 1806 pathSegments.last.isNotEmpty) { | 1793 pathSegments.last.isNotEmpty) { |
| 1807 pathSegments.add(""); // Extra separator at end. | 1794 pathSegments.add(""); // Extra separator at end. |
| 1808 } | 1795 } |
| 1809 return new Uri(pathSegments: pathSegments); | 1796 return new Uri(pathSegments: pathSegments); |
| 1810 } | 1797 } |
| 1811 } | 1798 } |
| 1812 | 1799 |
| 1813 Uri replace({String scheme, | 1800 Uri replace( |
| 1814 String userInfo, | 1801 {String scheme, |
| 1815 String host, | 1802 String userInfo, |
| 1816 int port, | 1803 String host, |
| 1817 String path, | 1804 int port, |
| 1818 Iterable<String> pathSegments, | 1805 String path, |
| 1819 String query, | 1806 Iterable<String> pathSegments, |
| 1820 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, | 1807 String query, |
| 1821 String fragment}) { | 1808 Map<String, dynamic /*String|Iterable<String>*/ > queryParameters, |
| 1809 String fragment}) { |
| 1822 // Set to true if the scheme has (potentially) changed. | 1810 // Set to true if the scheme has (potentially) changed. |
| 1823 // In that case, the default port may also have changed and we need | 1811 // In that case, the default port may also have changed and we need |
| 1824 // to check even the existing port. | 1812 // to check even the existing port. |
| 1825 bool schemeChanged = false; | 1813 bool schemeChanged = false; |
| 1826 if (scheme != null) { | 1814 if (scheme != null) { |
| 1827 scheme = _makeScheme(scheme, 0, scheme.length); | 1815 scheme = _makeScheme(scheme, 0, scheme.length); |
| 1828 schemeChanged = (scheme != this.scheme); | 1816 schemeChanged = (scheme != this.scheme); |
| 1829 } else { | 1817 } else { |
| 1830 scheme = this.scheme; | 1818 scheme = this.scheme; |
| 1831 } | 1819 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1847 if (host != null) { | 1835 if (host != null) { |
| 1848 host = _makeHost(host, 0, host.length, false); | 1836 host = _makeHost(host, 0, host.length, false); |
| 1849 } else if (this.hasAuthority) { | 1837 } else if (this.hasAuthority) { |
| 1850 host = this._host; | 1838 host = this._host; |
| 1851 } else if (userInfo.isNotEmpty || port != null || isFile) { | 1839 } else if (userInfo.isNotEmpty || port != null || isFile) { |
| 1852 host = ""; | 1840 host = ""; |
| 1853 } | 1841 } |
| 1854 | 1842 |
| 1855 bool hasAuthority = host != null; | 1843 bool hasAuthority = host != null; |
| 1856 if (path != null || pathSegments != null) { | 1844 if (path != null || pathSegments != null) { |
| 1857 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, | 1845 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, scheme, |
| 1858 scheme, hasAuthority); | 1846 hasAuthority); |
| 1859 } else { | 1847 } else { |
| 1860 path = this.path; | 1848 path = this.path; |
| 1861 if ((isFile || (hasAuthority && !path.isEmpty)) && | 1849 if ((isFile || (hasAuthority && !path.isEmpty)) && |
| 1862 !path.startsWith('/')) { | 1850 !path.startsWith('/')) { |
| 1863 path = "/" + path; | 1851 path = "/" + path; |
| 1864 } | 1852 } |
| 1865 } | 1853 } |
| 1866 | 1854 |
| 1867 if (query != null || queryParameters != null) { | 1855 if (query != null || queryParameters != null) { |
| 1868 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); | 1856 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); |
| 1869 } else { | 1857 } else { |
| 1870 query = this._query; | 1858 query = this._query; |
| 1871 } | 1859 } |
| 1872 | 1860 |
| 1873 if (fragment != null) { | 1861 if (fragment != null) { |
| 1874 fragment = _makeFragment(fragment, 0, fragment.length); | 1862 fragment = _makeFragment(fragment, 0, fragment.length); |
| 1875 } else { | 1863 } else { |
| 1876 fragment = this._fragment; | 1864 fragment = this._fragment; |
| 1877 } | 1865 } |
| 1878 | 1866 |
| 1879 return new _Uri._internal( | 1867 return new _Uri._internal( |
| 1880 scheme, userInfo, host, port, path, query, fragment); | 1868 scheme, userInfo, host, port, path, query, fragment); |
| 1881 } | 1869 } |
| 1882 | 1870 |
| 1883 Uri removeFragment() { | 1871 Uri removeFragment() { |
| 1884 if (!this.hasFragment) return this; | 1872 if (!this.hasFragment) return this; |
| 1885 return new _Uri._internal(scheme, _userInfo, _host, _port, | 1873 return new _Uri._internal( |
| 1886 path, _query, null); | 1874 scheme, _userInfo, _host, _port, path, _query, null); |
| 1887 } | 1875 } |
| 1888 | 1876 |
| 1889 List<String> get pathSegments { | 1877 List<String> get pathSegments { |
| 1890 var result = _pathSegments; | 1878 var result = _pathSegments; |
| 1891 if (result != null) return result; | 1879 if (result != null) return result; |
| 1892 | 1880 |
| 1893 var pathToSplit = path; | 1881 var pathToSplit = path; |
| 1894 if (pathToSplit.isNotEmpty && pathToSplit.codeUnitAt(0) == _SLASH) { | 1882 if (pathToSplit.isNotEmpty && pathToSplit.codeUnitAt(0) == _SLASH) { |
| 1895 pathToSplit = pathToSplit.substring(1); | 1883 pathToSplit = pathToSplit.substring(1); |
| 1896 } | 1884 } |
| 1897 result = (pathToSplit == "") | 1885 result = (pathToSplit == "") |
| 1898 ? const<String>[] | 1886 ? const <String>[] |
| 1899 : new List<String>.unmodifiable( | 1887 : new List<String>.unmodifiable( |
| 1900 pathToSplit.split("/").map(Uri.decodeComponent)); | 1888 pathToSplit.split("/").map(Uri.decodeComponent)); |
| 1901 _pathSegments = result; | 1889 _pathSegments = result; |
| 1902 return result; | 1890 return result; |
| 1903 } | 1891 } |
| 1904 | 1892 |
| 1905 Map<String, String> get queryParameters { | 1893 Map<String, String> get queryParameters { |
| 1906 if (_queryParameters == null) { | 1894 if (_queryParameters == null) { |
| 1907 _queryParameters = | 1895 _queryParameters = |
| 1908 new UnmodifiableMapView<String, String>(Uri.splitQueryString(query)); | 1896 new UnmodifiableMapView<String, String>(Uri.splitQueryString(query)); |
| 1909 } | 1897 } |
| 1910 return _queryParameters; | 1898 return _queryParameters; |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2089 if (scheme == "package") return "package"; | 2077 if (scheme == "package") return "package"; |
| 2090 return scheme; | 2078 return scheme; |
| 2091 } | 2079 } |
| 2092 | 2080 |
| 2093 static String _makeUserInfo(String userInfo, int start, int end) { | 2081 static String _makeUserInfo(String userInfo, int start, int end) { |
| 2094 if (userInfo == null) return ""; | 2082 if (userInfo == null) return ""; |
| 2095 return _normalizeOrSubstring(userInfo, start, end, _userinfoTable); | 2083 return _normalizeOrSubstring(userInfo, start, end, _userinfoTable); |
| 2096 } | 2084 } |
| 2097 | 2085 |
| 2098 static String _makePath(String path, int start, int end, | 2086 static String _makePath(String path, int start, int end, |
| 2099 Iterable<String> pathSegments, | 2087 Iterable<String> pathSegments, String scheme, bool hasAuthority) { |
| 2100 String scheme, | |
| 2101 bool hasAuthority) { | |
| 2102 bool isFile = (scheme == "file"); | 2088 bool isFile = (scheme == "file"); |
| 2103 bool ensureLeadingSlash = isFile || hasAuthority; | 2089 bool ensureLeadingSlash = isFile || hasAuthority; |
| 2104 if (path == null && pathSegments == null) return isFile ? "/" : ""; | 2090 if (path == null && pathSegments == null) return isFile ? "/" : ""; |
| 2105 if (path != null && pathSegments != null) { | 2091 if (path != null && pathSegments != null) { |
| 2106 throw new ArgumentError('Both path and pathSegments specified'); | 2092 throw new ArgumentError('Both path and pathSegments specified'); |
| 2107 } | 2093 } |
| 2108 var result; | 2094 var result; |
| 2109 if (path != null) { | 2095 if (path != null) { |
| 2110 result = _normalizeOrSubstring(path, start, end, _pathCharOrSlashTable); | 2096 result = _normalizeOrSubstring(path, start, end, _pathCharOrSlashTable); |
| 2111 } else { | 2097 } else { |
| 2112 result = pathSegments.map((s) => | 2098 result = pathSegments |
| 2113 _uriEncode(_pathCharTable, s, UTF8, false)).join("/"); | 2099 .map((s) => _uriEncode(_pathCharTable, s, UTF8, false)) |
| 2100 .join("/"); |
| 2114 } | 2101 } |
| 2115 if (result.isEmpty) { | 2102 if (result.isEmpty) { |
| 2116 if (isFile) return "/"; | 2103 if (isFile) return "/"; |
| 2117 } else if (ensureLeadingSlash && !result.startsWith('/')) { | 2104 } else if (ensureLeadingSlash && !result.startsWith('/')) { |
| 2118 result = "/" + result; | 2105 result = "/" + result; |
| 2119 } | 2106 } |
| 2120 result = _normalizePath(result, scheme, hasAuthority); | 2107 result = _normalizePath(result, scheme, hasAuthority); |
| 2121 return result; | 2108 return result; |
| 2122 } | 2109 } |
| 2123 | 2110 |
| 2124 /// Performs path normalization (remove dot segments) on a path. | 2111 /// Performs path normalization (remove dot segments) on a path. |
| 2125 /// | 2112 /// |
| 2126 /// If the URI has neither scheme nor authority, it's considered a | 2113 /// If the URI has neither scheme nor authority, it's considered a |
| 2127 /// "pure path" and normalization won't remove leading ".." segments. | 2114 /// "pure path" and normalization won't remove leading ".." segments. |
| 2128 /// Otherwise it follows the RFC 3986 "remove dot segments" algorithm. | 2115 /// Otherwise it follows the RFC 3986 "remove dot segments" algorithm. |
| 2129 static String _normalizePath(String path, String scheme, bool hasAuthority) { | 2116 static String _normalizePath(String path, String scheme, bool hasAuthority) { |
| 2130 if (scheme.isEmpty && !hasAuthority && !path.startsWith('/')) { | 2117 if (scheme.isEmpty && !hasAuthority && !path.startsWith('/')) { |
| 2131 return _normalizeRelativePath(path, scheme.isNotEmpty || hasAuthority); | 2118 return _normalizeRelativePath(path, scheme.isNotEmpty || hasAuthority); |
| 2132 } | 2119 } |
| 2133 return _removeDotSegments(path); | 2120 return _removeDotSegments(path); |
| 2134 } | 2121 } |
| 2135 | 2122 |
| 2136 static String _makeQuery( | 2123 static String _makeQuery(String query, int start, int end, |
| 2137 String query, int start, int end, | 2124 Map<String, dynamic /*String|Iterable<String>*/ > queryParameters) { |
| 2138 Map<String, dynamic/*String|Iterable<String>*/> queryParameters) { | |
| 2139 if (query != null) { | 2125 if (query != null) { |
| 2140 if (queryParameters != null) { | 2126 if (queryParameters != null) { |
| 2141 throw new ArgumentError('Both query and queryParameters specified'); | 2127 throw new ArgumentError('Both query and queryParameters specified'); |
| 2142 } | 2128 } |
| 2143 return _normalizeOrSubstring(query, start, end, _queryCharTable); | 2129 return _normalizeOrSubstring(query, start, end, _queryCharTable); |
| 2144 } | 2130 } |
| 2145 if (queryParameters == null) return null; | 2131 if (queryParameters == null) return null; |
| 2146 | 2132 |
| 2147 var result = new StringBuffer(); | 2133 var result = new StringBuffer(); |
| 2148 var separator = ""; | 2134 var separator = ""; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2184 * | 2170 * |
| 2185 * Returns "%" if the escape is invalid (not two valid hex digits following | 2171 * Returns "%" if the escape is invalid (not two valid hex digits following |
| 2186 * the percent sign). The calling code should replace the percent | 2172 * the percent sign). The calling code should replace the percent |
| 2187 * sign with "%25", but leave the following two characters unmodified. | 2173 * sign with "%25", but leave the following two characters unmodified. |
| 2188 * | 2174 * |
| 2189 * If [lowerCase] is true, a single character returned is always lower case, | 2175 * If [lowerCase] is true, a single character returned is always lower case, |
| 2190 */ | 2176 */ |
| 2191 static String _normalizeEscape(String source, int index, bool lowerCase) { | 2177 static String _normalizeEscape(String source, int index, bool lowerCase) { |
| 2192 assert(source.codeUnitAt(index) == _PERCENT); | 2178 assert(source.codeUnitAt(index) == _PERCENT); |
| 2193 if (index + 2 >= source.length) { | 2179 if (index + 2 >= source.length) { |
| 2194 return "%"; // Marks the escape as invalid. | 2180 return "%"; // Marks the escape as invalid. |
| 2195 } | 2181 } |
| 2196 int firstDigit = source.codeUnitAt(index + 1); | 2182 int firstDigit = source.codeUnitAt(index + 1); |
| 2197 int secondDigit = source.codeUnitAt(index + 2); | 2183 int secondDigit = source.codeUnitAt(index + 2); |
| 2198 int firstDigitValue = hexDigitValue(firstDigit); | 2184 int firstDigitValue = hexDigitValue(firstDigit); |
| 2199 int secondDigitValue = hexDigitValue(secondDigit); | 2185 int secondDigitValue = hexDigitValue(secondDigit); |
| 2200 if (firstDigitValue < 0 || secondDigitValue < 0) { | 2186 if (firstDigitValue < 0 || secondDigitValue < 0) { |
| 2201 return "%"; // Marks the escape as invalid. | 2187 return "%"; // Marks the escape as invalid. |
| 2202 } | 2188 } |
| 2203 int value = firstDigitValue * 16 + secondDigitValue; | 2189 int value = firstDigitValue * 16 + secondDigitValue; |
| 2204 if (_isUnreservedChar(value)) { | 2190 if (_isUnreservedChar(value)) { |
| 2205 if (lowerCase && _UPPER_CASE_A <= value && _UPPER_CASE_Z >= value) { | 2191 if (lowerCase && _UPPER_CASE_A <= value && _UPPER_CASE_Z >= value) { |
| 2206 value |= 0x20; | 2192 value |= 0x20; |
| 2207 } | 2193 } |
| 2208 return new String.fromCharCode(value); | 2194 return new String.fromCharCode(value); |
| 2209 } | 2195 } |
| 2210 if (firstDigit >= _LOWER_CASE_A || secondDigit >= _LOWER_CASE_A) { | 2196 if (firstDigit >= _LOWER_CASE_A || secondDigit >= _LOWER_CASE_A) { |
| 2211 // Either digit is lower case. | 2197 // Either digit is lower case. |
| 2212 return source.substring(index, index + 3).toUpperCase(); | 2198 return source.substring(index, index + 3).toUpperCase(); |
| 2213 } | 2199 } |
| 2214 // Escape is retained, and is already non-lower case, so return null to | 2200 // Escape is retained, and is already non-lower case, so return null to |
| 2215 // represent "no replacement necessary". | 2201 // represent "no replacement necessary". |
| 2216 return null; | 2202 return null; |
| 2217 } | 2203 } |
| 2218 | 2204 |
| 2219 static String _escapeChar(int char) { | 2205 static String _escapeChar(int char) { |
| 2220 assert(char <= 0x10ffff); // It's a valid unicode code point. | 2206 assert(char <= 0x10ffff); // It's a valid unicode code point. |
| 2221 List<int> codeUnits; | 2207 List<int> codeUnits; |
| 2222 if (char < 0x80) { | 2208 if (char < 0x80) { |
| 2223 // ASCII, a single percent encoded sequence. | 2209 // ASCII, a single percent encoded sequence. |
| 2224 codeUnits = new List(3); | 2210 codeUnits = new List(3); |
| 2225 codeUnits[0] = _PERCENT; | 2211 codeUnits[0] = _PERCENT; |
| 2226 codeUnits[1] = _hexDigits.codeUnitAt(char >> 4); | 2212 codeUnits[1] = _hexDigits.codeUnitAt(char >> 4); |
| 2227 codeUnits[2] = _hexDigits.codeUnitAt(char & 0xf); | 2213 codeUnits[2] = _hexDigits.codeUnitAt(char & 0xf); |
| 2228 } else { | 2214 } else { |
| 2229 // Do UTF-8 encoding of character, then percent encode bytes. | 2215 // Do UTF-8 encoding of character, then percent encode bytes. |
| 2230 int flag = 0xc0; // The high-bit markers on the first byte of UTF-8. | 2216 int flag = 0xc0; // The high-bit markers on the first byte of UTF-8. |
| 2231 int encodedBytes = 2; | 2217 int encodedBytes = 2; |
| 2232 if (char > 0x7ff) { | 2218 if (char > 0x7ff) { |
| 2233 flag = 0xe0; | 2219 flag = 0xe0; |
| 2234 encodedBytes = 3; | 2220 encodedBytes = 3; |
| 2235 if (char > 0xffff) { | 2221 if (char > 0xffff) { |
| 2236 encodedBytes = 4; | 2222 encodedBytes = 4; |
| 2237 flag = 0xf0; | 2223 flag = 0xf0; |
| 2238 } | 2224 } |
| 2239 } | 2225 } |
| 2240 codeUnits = new List(3 * encodedBytes); | 2226 codeUnits = new List(3 * encodedBytes); |
| 2241 int index = 0; | 2227 int index = 0; |
| 2242 while (--encodedBytes >= 0) { | 2228 while (--encodedBytes >= 0) { |
| 2243 int byte = ((char >> (6 * encodedBytes)) & 0x3f) | flag; | 2229 int byte = ((char >> (6 * encodedBytes)) & 0x3f) | flag; |
| 2244 codeUnits[index] = _PERCENT; | 2230 codeUnits[index] = _PERCENT; |
| 2245 codeUnits[index + 1] = _hexDigits.codeUnitAt(byte >> 4); | 2231 codeUnits[index + 1] = _hexDigits.codeUnitAt(byte >> 4); |
| 2246 codeUnits[index + 2] = _hexDigits.codeUnitAt(byte & 0xf); | 2232 codeUnits[index + 2] = _hexDigits.codeUnitAt(byte & 0xf); |
| 2247 index += 3; | 2233 index += 3; |
| 2248 flag = 0x80; // Following bytes have only high bit set. | 2234 flag = 0x80; // Following bytes have only high bit set. |
| 2249 } | 2235 } |
| 2250 } | 2236 } |
| 2251 return new String.fromCharCodes(codeUnits); | 2237 return new String.fromCharCodes(codeUnits); |
| 2252 } | 2238 } |
| 2253 | 2239 |
| 2254 /** | 2240 /** |
| 2255 * Normalizes using [_normalize] or returns substring of original. | 2241 * Normalizes using [_normalize] or returns substring of original. |
| 2256 * | 2242 * |
| 2257 * If [_normalize] returns `null` (original content is already normalized), | 2243 * If [_normalize] returns `null` (original content is already normalized), |
| 2258 * this methods returns the substring if [component] from [start] to [end]. | 2244 * this methods returns the substring if [component] from [start] to [end]. |
| 2259 */ | 2245 */ |
| 2260 static String _normalizeOrSubstring(String component, int start, int end, | 2246 static String _normalizeOrSubstring( |
| 2261 List<int> charTable) { | 2247 String component, int start, int end, List<int> charTable) { |
| 2262 return _normalize(component, start, end, charTable) ?? | 2248 return _normalize(component, start, end, charTable) ?? |
| 2263 component.substring(start, end); | 2249 component.substring(start, end); |
| 2264 } | 2250 } |
| 2265 | 2251 |
| 2266 /** | 2252 /** |
| 2267 * Runs through component checking that each character is valid and | 2253 * Runs through component checking that each character is valid and |
| 2268 * normalize percent escapes. | 2254 * normalize percent escapes. |
| 2269 * | 2255 * |
| 2270 * Uses [charTable] to check if a non-`%` character is allowed. | 2256 * Uses [charTable] to check if a non-`%` character is allowed. |
| 2271 * Each `%` character must be followed by two hex digits. | 2257 * Each `%` character must be followed by two hex digits. |
| 2272 * If the hex-digits are lower case letters, they are converted to | 2258 * If the hex-digits are lower case letters, they are converted to |
| 2273 * upper case. | 2259 * upper case. |
| 2274 * | 2260 * |
| 2275 * Returns `null` if the original content was already normalized. | 2261 * Returns `null` if the original content was already normalized. |
| 2276 */ | 2262 */ |
| 2277 static String _normalize(String component, int start, int end, | 2263 static String _normalize( |
| 2278 List<int> charTable, | 2264 String component, int start, int end, List<int> charTable, |
| 2279 {bool escapeDelimiters = false}) { | 2265 {bool escapeDelimiters = false}) { |
| 2280 StringBuffer buffer; | 2266 StringBuffer buffer; |
| 2281 int sectionStart = start; | 2267 int sectionStart = start; |
| 2282 int index = start; | 2268 int index = start; |
| 2283 // Loop while characters are valid and escapes correct and upper-case. | 2269 // Loop while characters are valid and escapes correct and upper-case. |
| 2284 while (index < end) { | 2270 while (index < end) { |
| 2285 int char = component.codeUnitAt(index); | 2271 int char = component.codeUnitAt(index); |
| 2286 if (char < 127 && (charTable[char >> 4] & (1 << (char & 0x0f))) != 0) { | 2272 if (char < 127 && (charTable[char >> 4] & (1 << (char & 0x0f))) != 0) { |
| 2287 index++; | 2273 index++; |
| 2288 } else { | 2274 } else { |
| 2289 String replacement; | 2275 String replacement; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2371 // If we see a "." or ".." segment in base, stop here and let | 2357 // If we see a "." or ".." segment in base, stop here and let |
| 2372 // _removeDotSegments handle it. | 2358 // _removeDotSegments handle it. |
| 2373 if ((delta == 2 || delta == 3) && | 2359 if ((delta == 2 || delta == 3) && |
| 2374 base.codeUnitAt(newEnd + 1) == _DOT && | 2360 base.codeUnitAt(newEnd + 1) == _DOT && |
| 2375 (delta == 2 || base.codeUnitAt(newEnd + 2) == _DOT)) { | 2361 (delta == 2 || base.codeUnitAt(newEnd + 2) == _DOT)) { |
| 2376 break; | 2362 break; |
| 2377 } | 2363 } |
| 2378 baseEnd = newEnd; | 2364 baseEnd = newEnd; |
| 2379 backCount--; | 2365 backCount--; |
| 2380 } | 2366 } |
| 2381 return base.replaceRange(baseEnd + 1, null, | 2367 return base.replaceRange( |
| 2382 reference.substring(refStart - 3 * backCount)); | 2368 baseEnd + 1, null, reference.substring(refStart - 3 * backCount)); |
| 2383 } | 2369 } |
| 2384 | 2370 |
| 2385 /// Make a guess at whether a path contains a `..` or `.` segment. | 2371 /// Make a guess at whether a path contains a `..` or `.` segment. |
| 2386 /// | 2372 /// |
| 2387 /// This is a primitive test that can cause false positives. | 2373 /// This is a primitive test that can cause false positives. |
| 2388 /// It's only used to avoid a more expensive operation in the case where | 2374 /// It's only used to avoid a more expensive operation in the case where |
| 2389 /// it's not necessary. | 2375 /// it's not necessary. |
| 2390 static bool _mayContainDotSegments(String path) { | 2376 static bool _mayContainDotSegments(String path) { |
| 2391 if (path.startsWith('.')) return true; | 2377 if (path.startsWith('.')) return true; |
| 2392 int index = path.indexOf("/."); | 2378 int index = path.indexOf("/."); |
| 2393 return index != -1; | 2379 return index != -1; |
| 2394 } | 2380 } |
| 2395 | 2381 |
| 2396 /// Removes '.' and '..' segments from a path. | 2382 /// Removes '.' and '..' segments from a path. |
| 2397 /// | 2383 /// |
| 2398 /// Follows the RFC 2986 "remove dot segments" algorithm. | 2384 /// Follows the RFC 2986 "remove dot segments" algorithm. |
| 2399 /// This algorithm is only used on paths of URIs with a scheme, | 2385 /// This algorithm is only used on paths of URIs with a scheme, |
| 2400 /// and it treats the path as if it is absolute (leading '..' are removed). | 2386 /// and it treats the path as if it is absolute (leading '..' are removed). |
| 2401 static String _removeDotSegments(String path) { | 2387 static String _removeDotSegments(String path) { |
| 2402 if (!_mayContainDotSegments(path)) return path; | 2388 if (!_mayContainDotSegments(path)) return path; |
| 2403 assert(path.isNotEmpty); // An empty path would not have dot segments. | 2389 assert(path.isNotEmpty); // An empty path would not have dot segments. |
| 2404 List<String> output = []; | 2390 List<String> output = []; |
| 2405 bool appendSlash = false; | 2391 bool appendSlash = false; |
| 2406 for (String segment in path.split("/")) { | 2392 for (String segment in path.split("/")) { |
| 2407 appendSlash = false; | 2393 appendSlash = false; |
| 2408 if (segment == "..") { | 2394 if (segment == "..") { |
| 2409 if (output.isNotEmpty) { | 2395 if (output.isNotEmpty) { |
| 2410 output.removeLast(); | 2396 output.removeLast(); |
| 2411 if (output.isEmpty) { | 2397 if (output.isEmpty) { |
| 2412 output.add(""); | 2398 output.add(""); |
| 2413 } | 2399 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 2428 /// If the path starts with something that looks like a scheme, | 2414 /// If the path starts with something that looks like a scheme, |
| 2429 /// and [allowScheme] is false, the colon is escaped. | 2415 /// and [allowScheme] is false, the colon is escaped. |
| 2430 /// | 2416 /// |
| 2431 /// Removing the ".." from a "bar/foo/.." sequence results in "bar/" | 2417 /// Removing the ".." from a "bar/foo/.." sequence results in "bar/" |
| 2432 /// (trailing "/"). If the entire path is removed (because it contains as | 2418 /// (trailing "/"). If the entire path is removed (because it contains as |
| 2433 /// many ".." segments as real segments), the result is "./". | 2419 /// many ".." segments as real segments), the result is "./". |
| 2434 /// This is different from an empty string, which represents "no path", | 2420 /// This is different from an empty string, which represents "no path", |
| 2435 /// when you resolve it against a base URI with a path with a non-empty | 2421 /// when you resolve it against a base URI with a path with a non-empty |
| 2436 /// final segment. | 2422 /// final segment. |
| 2437 static String _normalizeRelativePath(String path, bool allowScheme) { | 2423 static String _normalizeRelativePath(String path, bool allowScheme) { |
| 2438 assert(!path.startsWith('/')); // Only get called for relative paths. | 2424 assert(!path.startsWith('/')); // Only get called for relative paths. |
| 2439 if (!_mayContainDotSegments(path)) { | 2425 if (!_mayContainDotSegments(path)) { |
| 2440 if (!allowScheme) path = _escapeScheme(path); | 2426 if (!allowScheme) path = _escapeScheme(path); |
| 2441 return path; | 2427 return path; |
| 2442 } | 2428 } |
| 2443 assert(path.isNotEmpty); // An empty path would not have dot segments. | 2429 assert(path.isNotEmpty); // An empty path would not have dot segments. |
| 2444 List<String> output = []; | 2430 List<String> output = []; |
| 2445 bool appendSlash = false; | 2431 bool appendSlash = false; |
| 2446 for (String segment in path.split("/")) { | 2432 for (String segment in path.split("/")) { |
| 2447 appendSlash = false; | 2433 appendSlash = false; |
| 2448 if (".." == segment) { | 2434 if (".." == segment) { |
| 2449 if (!output.isEmpty && output.last != "..") { | 2435 if (!output.isEmpty && output.last != "..") { |
| 2450 output.removeLast(); | 2436 output.removeLast(); |
| 2451 appendSlash = true; | 2437 appendSlash = true; |
| 2452 } else { | 2438 } else { |
| 2453 output.add(".."); | 2439 output.add(".."); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2504 } | 2490 } |
| 2505 targetPath = _removeDotSegments(reference.path); | 2491 targetPath = _removeDotSegments(reference.path); |
| 2506 if (reference.hasQuery) { | 2492 if (reference.hasQuery) { |
| 2507 targetQuery = reference.query; | 2493 targetQuery = reference.query; |
| 2508 } | 2494 } |
| 2509 } else { | 2495 } else { |
| 2510 targetScheme = this.scheme; | 2496 targetScheme = this.scheme; |
| 2511 if (reference.hasAuthority) { | 2497 if (reference.hasAuthority) { |
| 2512 targetUserInfo = reference.userInfo; | 2498 targetUserInfo = reference.userInfo; |
| 2513 targetHost = reference.host; | 2499 targetHost = reference.host; |
| 2514 targetPort = _makePort(reference.hasPort ? reference.port : null, | 2500 targetPort = |
| 2515 targetScheme); | 2501 _makePort(reference.hasPort ? reference.port : null, targetScheme); |
| 2516 targetPath = _removeDotSegments(reference.path); | 2502 targetPath = _removeDotSegments(reference.path); |
| 2517 if (reference.hasQuery) targetQuery = reference.query; | 2503 if (reference.hasQuery) targetQuery = reference.query; |
| 2518 } else { | 2504 } else { |
| 2519 targetUserInfo = this._userInfo; | 2505 targetUserInfo = this._userInfo; |
| 2520 targetHost = this._host; | 2506 targetHost = this._host; |
| 2521 targetPort = this._port; | 2507 targetPort = this._port; |
| 2522 if (reference.path == "") { | 2508 if (reference.path == "") { |
| 2523 targetPath = this.path; | 2509 targetPath = this.path; |
| 2524 if (reference.hasQuery) { | 2510 if (reference.hasQuery) { |
| 2525 targetQuery = reference.query; | 2511 targetQuery = reference.query; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 2547 } | 2533 } |
| 2548 } else { | 2534 } else { |
| 2549 var mergedPath = _mergePaths(this.path, reference.path); | 2535 var mergedPath = _mergePaths(this.path, reference.path); |
| 2550 if (this.hasScheme || this.hasAuthority || this.hasAbsolutePath) { | 2536 if (this.hasScheme || this.hasAuthority || this.hasAbsolutePath) { |
| 2551 targetPath = _removeDotSegments(mergedPath); | 2537 targetPath = _removeDotSegments(mergedPath); |
| 2552 } else { | 2538 } else { |
| 2553 // Non-RFC 3986 behavior. | 2539 // Non-RFC 3986 behavior. |
| 2554 // If both base and reference are relative paths, | 2540 // If both base and reference are relative paths, |
| 2555 // allow the merged path to start with "..". | 2541 // allow the merged path to start with "..". |
| 2556 // The RFC only specifies the case where the base has a scheme. | 2542 // The RFC only specifies the case where the base has a scheme. |
| 2557 targetPath = _normalizeRelativePath(mergedPath, | 2543 targetPath = _normalizeRelativePath( |
| 2558 this.hasScheme || this.hasAuthority); | 2544 mergedPath, this.hasScheme || this.hasAuthority); |
| 2559 } | 2545 } |
| 2560 } | 2546 } |
| 2561 } | 2547 } |
| 2562 if (reference.hasQuery) targetQuery = reference.query; | 2548 if (reference.hasQuery) targetQuery = reference.query; |
| 2563 } | 2549 } |
| 2564 } | 2550 } |
| 2565 } | 2551 } |
| 2566 String fragment = reference.hasFragment ? reference.fragment : null; | 2552 String fragment = reference.hasFragment ? reference.fragment : null; |
| 2567 return new _Uri._internal(targetScheme, | 2553 return new _Uri._internal(targetScheme, targetUserInfo, targetHost, |
| 2568 targetUserInfo, | 2554 targetPort, targetPath, targetQuery, fragment); |
| 2569 targetHost, | |
| 2570 targetPort, | |
| 2571 targetPath, | |
| 2572 targetQuery, | |
| 2573 fragment); | |
| 2574 } | 2555 } |
| 2575 | 2556 |
| 2576 bool get hasScheme => scheme.isNotEmpty; | 2557 bool get hasScheme => scheme.isNotEmpty; |
| 2577 | 2558 |
| 2578 bool get hasAuthority => _host != null; | 2559 bool get hasAuthority => _host != null; |
| 2579 | 2560 |
| 2580 bool get hasPort => _port != null; | 2561 bool get hasPort => _port != null; |
| 2581 | 2562 |
| 2582 bool get hasQuery => _query != null; | 2563 bool get hasQuery => _query != null; |
| 2583 | 2564 |
| 2584 bool get hasFragment => _fragment != null; | 2565 bool get hasFragment => _fragment != null; |
| 2585 | 2566 |
| 2586 bool get hasEmptyPath => path.isEmpty; | 2567 bool get hasEmptyPath => path.isEmpty; |
| 2587 | 2568 |
| 2588 bool get hasAbsolutePath => path.startsWith('/'); | 2569 bool get hasAbsolutePath => path.startsWith('/'); |
| 2589 | 2570 |
| 2590 String get origin { | 2571 String get origin { |
| 2591 if (scheme == "") { | 2572 if (scheme == "") { |
| 2592 throw new StateError("Cannot use origin without a scheme: $this"); | 2573 throw new StateError("Cannot use origin without a scheme: $this"); |
| 2593 } | 2574 } |
| 2594 if (scheme != "http" && scheme != "https") { | 2575 if (scheme != "http" && scheme != "https") { |
| 2595 throw new StateError( | 2576 throw new StateError( |
| 2596 "Origin is only applicable schemes http and https: $this"); | 2577 "Origin is only applicable schemes http and https: $this"); |
| 2597 } | 2578 } |
| 2598 if (_host == null || _host == "") { | 2579 if (_host == null || _host == "") { |
| 2599 throw new StateError( | 2580 throw new StateError( |
| 2600 "A $scheme: URI should have a non-empty host name: $this"); | 2581 "A $scheme: URI should have a non-empty host name: $this"); |
| 2601 } | 2582 } |
| 2602 if (_port == null) return "$scheme://$_host"; | 2583 if (_port == null) return "$scheme://$_host"; |
| 2603 return "$scheme://$_host:$_port"; | 2584 return "$scheme://$_host:$_port"; |
| 2604 } | 2585 } |
| 2605 | 2586 |
| 2606 String toFilePath({bool windows}) { | 2587 String toFilePath({bool windows}) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2701 // The empty URI means "localhost". | 2682 // The empty URI means "localhost". |
| 2702 sb.write("//"); | 2683 sb.write("//"); |
| 2703 _writeAuthority(sb); | 2684 _writeAuthority(sb); |
| 2704 } | 2685 } |
| 2705 sb.write(path); | 2686 sb.write(path); |
| 2706 if (_query != null) sb..write("?")..write(_query); | 2687 if (_query != null) sb..write("?")..write(_query); |
| 2707 if (_fragment != null) sb..write("#")..write(_fragment); | 2688 if (_fragment != null) sb..write("#")..write(_fragment); |
| 2708 return sb.toString(); | 2689 return sb.toString(); |
| 2709 } | 2690 } |
| 2710 | 2691 |
| 2711 bool operator==(other) { | 2692 bool operator ==(other) { |
| 2712 if (identical(this, other)) return true; | 2693 if (identical(this, other)) return true; |
| 2713 if (other is Uri) { | 2694 if (other is Uri) { |
| 2714 Uri uri = other; | 2695 Uri uri = other; |
| 2715 return scheme == uri.scheme && | 2696 return scheme == uri.scheme && |
| 2716 hasAuthority == uri.hasAuthority && | 2697 hasAuthority == uri.hasAuthority && |
| 2717 userInfo == uri.userInfo && | 2698 userInfo == uri.userInfo && |
| 2718 host == uri.host && | 2699 host == uri.host && |
| 2719 port == uri.port && | 2700 port == uri.port && |
| 2720 path == uri.path && | 2701 path == uri.path && |
| 2721 hasQuery == uri.hasQuery && | 2702 hasQuery == uri.hasQuery && |
| 2722 query == uri.query && | 2703 query == uri.query && |
| 2723 hasFragment == uri.hasFragment && | 2704 hasFragment == uri.hasFragment && |
| 2724 fragment == uri.fragment; | 2705 fragment == uri.fragment; |
| 2725 } | 2706 } |
| 2726 return false; | 2707 return false; |
| 2727 } | 2708 } |
| 2728 | 2709 |
| 2729 int get hashCode { | 2710 int get hashCode { |
| 2730 return _hashCodeCache ??= toString().hashCode; | 2711 return _hashCodeCache ??= toString().hashCode; |
| 2731 } | 2712 } |
| 2732 | 2713 |
| 2733 static List _createList() => []; | 2714 static List _createList() => []; |
| 2734 | 2715 |
| 2735 static Map _splitQueryStringAll( | 2716 static Map _splitQueryStringAll(String query, {Encoding encoding: UTF8}) { |
| 2736 String query, {Encoding encoding: UTF8}) { | |
| 2737 Map result = {}; | 2717 Map result = {}; |
| 2738 int i = 0; | 2718 int i = 0; |
| 2739 int start = 0; | 2719 int start = 0; |
| 2740 int equalsIndex = -1; | 2720 int equalsIndex = -1; |
| 2741 | 2721 |
| 2742 void parsePair(int start, int equalsIndex, int end) { | 2722 void parsePair(int start, int equalsIndex, int end) { |
| 2743 String key; | 2723 String key; |
| 2744 String value; | 2724 String value; |
| 2745 if (start == end) return; | 2725 if (start == end) return; |
| 2746 if (equalsIndex < 0) { | 2726 if (equalsIndex < 0) { |
| 2747 key = _uriDecode(query, start, end, encoding, true); | 2727 key = _uriDecode(query, start, end, encoding, true); |
| 2748 value = ""; | 2728 value = ""; |
| 2749 } else { | 2729 } else { |
| 2750 key = _uriDecode(query, start, equalsIndex, encoding, true); | 2730 key = _uriDecode(query, start, equalsIndex, encoding, true); |
| 2751 value = _uriDecode(query, equalsIndex + 1, end, encoding, true); | 2731 value = _uriDecode(query, equalsIndex + 1, end, encoding, true); |
| 2752 } | 2732 } |
| 2753 result.putIfAbsent(key, _createList).add(value); | 2733 result.putIfAbsent(key, _createList).add(value); |
| 2754 } | 2734 } |
| 2755 | 2735 |
| 2756 while (i < query.length) { | 2736 while (i < query.length) { |
| 2757 int char = query.codeUnitAt(i); | 2737 int char = query.codeUnitAt(i); |
| 2758 if (char == _EQUALS) { | 2738 if (char == _EQUALS) { |
| 2759 if (equalsIndex < 0) equalsIndex = i; | 2739 if (equalsIndex < 0) equalsIndex = i; |
| 2760 } else if (char == _AMPERSAND) { | 2740 } else if (char == _AMPERSAND) { |
| 2761 parsePair(start, equalsIndex, i); | 2741 parsePair(start, equalsIndex, i); |
| 2762 start = i + 1; | 2742 start = i + 1; |
| 2763 equalsIndex = -1; | 2743 equalsIndex = -1; |
| 2764 } | 2744 } |
| 2765 i++; | 2745 i++; |
| 2766 } | 2746 } |
| 2767 parsePair(start, equalsIndex, i); | 2747 parsePair(start, equalsIndex, i); |
| 2768 return result; | 2748 return result; |
| 2769 } | 2749 } |
| 2770 | 2750 |
| 2771 external static String _uriEncode(List<int> canonicalTable, | 2751 external static String _uriEncode(List<int> canonicalTable, String text, |
| 2772 String text, | 2752 Encoding encoding, bool spaceToPlus); |
| 2773 Encoding encoding, | |
| 2774 bool spaceToPlus); | |
| 2775 | 2753 |
| 2776 /** | 2754 /** |
| 2777 * Convert a byte (2 character hex sequence) in string [s] starting | 2755 * Convert a byte (2 character hex sequence) in string [s] starting |
| 2778 * at position [pos] to its ordinal value | 2756 * at position [pos] to its ordinal value |
| 2779 */ | 2757 */ |
| 2780 static int _hexCharPairToByte(String s, int pos) { | 2758 static int _hexCharPairToByte(String s, int pos) { |
| 2781 int byte = 0; | 2759 int byte = 0; |
| 2782 for (int i = 0; i < 2; i++) { | 2760 for (int i = 0; i < 2; i++) { |
| 2783 var charCode = s.codeUnitAt(pos + i); | 2761 var charCode = s.codeUnitAt(pos + i); |
| 2784 if (0x30 <= charCode && charCode <= 0x39) { | 2762 if (0x30 <= charCode && charCode <= 0x39) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 2801 * | 2779 * |
| 2802 * It unescapes the string [text] and returns the unescaped string. | 2780 * It unescapes the string [text] and returns the unescaped string. |
| 2803 * | 2781 * |
| 2804 * This function is similar to the JavaScript-function `decodeURI`. | 2782 * This function is similar to the JavaScript-function `decodeURI`. |
| 2805 * | 2783 * |
| 2806 * If [plusToSpace] is `true`, plus characters will be converted to spaces. | 2784 * If [plusToSpace] is `true`, plus characters will be converted to spaces. |
| 2807 * | 2785 * |
| 2808 * The decoder will create a byte-list of the percent-encoded parts, and then | 2786 * The decoder will create a byte-list of the percent-encoded parts, and then |
| 2809 * decode the byte-list using [encoding]. The default encodings UTF-8. | 2787 * decode the byte-list using [encoding]. The default encodings UTF-8. |
| 2810 */ | 2788 */ |
| 2811 static String _uriDecode(String text, | 2789 static String _uriDecode( |
| 2812 int start, | 2790 String text, int start, int end, Encoding encoding, bool plusToSpace) { |
| 2813 int end, | |
| 2814 Encoding encoding, | |
| 2815 bool plusToSpace) { | |
| 2816 assert(0 <= start); | 2791 assert(0 <= start); |
| 2817 assert(start <= end); | 2792 assert(start <= end); |
| 2818 assert(end <= text.length); | 2793 assert(end <= text.length); |
| 2819 assert(encoding != null); | 2794 assert(encoding != null); |
| 2820 // First check whether there is any characters which need special handling. | 2795 // First check whether there is any characters which need special handling. |
| 2821 bool simple = true; | 2796 bool simple = true; |
| 2822 for (int i = start; i < end; i++) { | 2797 for (int i = start; i < end; i++) { |
| 2823 var codeUnit = text.codeUnitAt(i); | 2798 var codeUnit = text.codeUnitAt(i); |
| 2824 if (codeUnit > 127 || | 2799 if (codeUnit > 127 || |
| 2825 codeUnit == _PERCENT || | 2800 codeUnit == _PERCENT || |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2858 return encoding.decode(bytes); | 2833 return encoding.decode(bytes); |
| 2859 } | 2834 } |
| 2860 | 2835 |
| 2861 static bool _isAlphabeticCharacter(int codeUnit) { | 2836 static bool _isAlphabeticCharacter(int codeUnit) { |
| 2862 var lowerCase = codeUnit | 0x20; | 2837 var lowerCase = codeUnit | 0x20; |
| 2863 return (_LOWER_CASE_A <= lowerCase && lowerCase <= _LOWER_CASE_Z); | 2838 return (_LOWER_CASE_A <= lowerCase && lowerCase <= _LOWER_CASE_Z); |
| 2864 } | 2839 } |
| 2865 | 2840 |
| 2866 static bool _isUnreservedChar(int char) { | 2841 static bool _isUnreservedChar(int char) { |
| 2867 return char < 127 && | 2842 return char < 127 && |
| 2868 ((_unreservedTable[char >> 4] & (1 << (char & 0x0f))) != 0); | 2843 ((_unreservedTable[char >> 4] & (1 << (char & 0x0f))) != 0); |
| 2869 } | 2844 } |
| 2870 | 2845 |
| 2871 // Tables of char-codes organized as a bit vector of 128 bits where | 2846 // Tables of char-codes organized as a bit vector of 128 bits where |
| 2872 // each bit indicate whether a character code on the 0-127 needs to | 2847 // each bit indicate whether a character code on the 0-127 needs to |
| 2873 // be escaped or not. | 2848 // be escaped or not. |
| 2874 | 2849 |
| 2875 // The unreserved characters of RFC 3986. | 2850 // The unreserved characters of RFC 3986. |
| 2876 static const _unreservedTable = const [ | 2851 static const _unreservedTable = const [ |
| 2877 // LSB MSB | 2852 // LSB MSB |
| 2878 // | | | 2853 // | | |
| 2879 0x0000, // 0x00 - 0x0f 0000000000000000 | 2854 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 2880 0x0000, // 0x10 - 0x1f 0000000000000000 | 2855 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 2881 // -. | 2856 // -. |
| 2882 0x6000, // 0x20 - 0x2f 0000000000000110 | 2857 0x6000, // 0x20 - 0x2f 0000000000000110 |
| 2883 // 0123456789 | 2858 // 0123456789 |
| 2884 0x03ff, // 0x30 - 0x3f 1111111111000000 | 2859 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| 2885 // ABCDEFGHIJKLMNO | 2860 // ABCDEFGHIJKLMNO |
| 2886 0xfffe, // 0x40 - 0x4f 0111111111111111 | 2861 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| 2887 // PQRSTUVWXYZ _ | 2862 // PQRSTUVWXYZ _ |
| 2888 0x87ff, // 0x50 - 0x5f 1111111111100001 | 2863 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 2889 // abcdefghijklmno | 2864 // abcdefghijklmno |
| 2890 0xfffe, // 0x60 - 0x6f 0111111111111111 | 2865 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 2891 // pqrstuvwxyz ~ | 2866 // pqrstuvwxyz ~ |
| 2892 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 2867 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 2868 ]; |
| 2893 | 2869 |
| 2894 // The unreserved characters of RFC 2396. | 2870 // The unreserved characters of RFC 2396. |
| 2895 static const _unreserved2396Table = const [ | 2871 static const _unreserved2396Table = const [ |
| 2896 // LSB MSB | 2872 // LSB MSB |
| 2897 // | | | 2873 // | | |
| 2898 0x0000, // 0x00 - 0x0f 0000000000000000 | 2874 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 2899 0x0000, // 0x10 - 0x1f 0000000000000000 | 2875 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 2900 // ! '()* -. | 2876 // ! '()* -. |
| 2901 0x6782, // 0x20 - 0x2f 0100000111100110 | 2877 0x6782, // 0x20 - 0x2f 0100000111100110 |
| 2902 // 0123456789 | 2878 // 0123456789 |
| 2903 0x03ff, // 0x30 - 0x3f 1111111111000000 | 2879 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| 2904 // ABCDEFGHIJKLMNO | 2880 // ABCDEFGHIJKLMNO |
| 2905 0xfffe, // 0x40 - 0x4f 0111111111111111 | 2881 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| 2906 // PQRSTUVWXYZ _ | 2882 // PQRSTUVWXYZ _ |
| 2907 0x87ff, // 0x50 - 0x5f 1111111111100001 | 2883 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 2908 // abcdefghijklmno | 2884 // abcdefghijklmno |
| 2909 0xfffe, // 0x60 - 0x6f 0111111111111111 | 2885 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 2910 // pqrstuvwxyz ~ | 2886 // pqrstuvwxyz ~ |
| 2911 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 2887 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 2888 ]; |
| 2912 | 2889 |
| 2913 // Table of reserved characters specified by ECMAScript 5. | 2890 // Table of reserved characters specified by ECMAScript 5. |
| 2914 static const _encodeFullTable = const [ | 2891 static const _encodeFullTable = const [ |
| 2915 // LSB MSB | 2892 // LSB MSB |
| 2916 // | | | 2893 // | | |
| 2917 0x0000, // 0x00 - 0x0f 0000000000000000 | 2894 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 2918 0x0000, // 0x10 - 0x1f 0000000000000000 | 2895 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 2919 // ! #$ &'()*+,-./ | 2896 // ! #$ &'()*+,-./ |
| 2920 0xffda, // 0x20 - 0x2f 0101101111111111 | 2897 0xffda, // 0x20 - 0x2f 0101101111111111 |
| 2921 // 0123456789:; = ? | 2898 // 0123456789:; = ? |
| 2922 0xafff, // 0x30 - 0x3f 1111111111110101 | 2899 0xafff, // 0x30 - 0x3f 1111111111110101 |
| 2923 // @ABCDEFGHIJKLMNO | 2900 // @ABCDEFGHIJKLMNO |
| 2924 0xffff, // 0x40 - 0x4f 1111111111111111 | 2901 0xffff, // 0x40 - 0x4f 1111111111111111 |
| 2925 // PQRSTUVWXYZ _ | 2902 // PQRSTUVWXYZ _ |
| 2926 0x87ff, // 0x50 - 0x5f 1111111111100001 | 2903 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 2927 // abcdefghijklmno | 2904 // abcdefghijklmno |
| 2928 0xfffe, // 0x60 - 0x6f 0111111111111111 | 2905 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 2929 // pqrstuvwxyz ~ | 2906 // pqrstuvwxyz ~ |
| 2930 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 2907 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 2908 ]; |
| 2931 | 2909 |
| 2932 // Characters allowed in the scheme. | 2910 // Characters allowed in the scheme. |
| 2933 static const _schemeTable = const [ | 2911 static const _schemeTable = const [ |
| 2934 // LSB MSB | 2912 // LSB MSB |
| 2935 // | | | 2913 // | | |
| 2936 0x0000, // 0x00 - 0x0f 0000000000000000 | 2914 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 2937 0x0000, // 0x10 - 0x1f 0000000000000000 | 2915 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 2938 // + -. | 2916 // + -. |
| 2939 0x6800, // 0x20 - 0x2f 0000000000010110 | 2917 0x6800, // 0x20 - 0x2f 0000000000010110 |
| 2940 // 0123456789 | 2918 // 0123456789 |
| 2941 0x03ff, // 0x30 - 0x3f 1111111111000000 | 2919 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| 2942 // ABCDEFGHIJKLMNO | 2920 // ABCDEFGHIJKLMNO |
| 2943 0xfffe, // 0x40 - 0x4f 0111111111111111 | 2921 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| 2944 // PQRSTUVWXYZ | 2922 // PQRSTUVWXYZ |
| 2945 0x07ff, // 0x50 - 0x5f 1111111111100001 | 2923 0x07ff, // 0x50 - 0x5f 1111111111100001 |
| 2946 // abcdefghijklmno | 2924 // abcdefghijklmno |
| 2947 0xfffe, // 0x60 - 0x6f 0111111111111111 | 2925 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 2948 // pqrstuvwxyz | 2926 // pqrstuvwxyz |
| 2949 0x07ff]; // 0x70 - 0x7f 1111111111100010 | 2927 0x07ff, // 0x70 - 0x7f 1111111111100010 |
| 2928 ]; |
| 2950 | 2929 |
| 2951 // Characters allowed in scheme except for upper case letters. | 2930 // Characters allowed in scheme except for upper case letters. |
| 2952 static const _schemeLowerTable = const [ | 2931 static const _schemeLowerTable = const [ |
| 2953 // LSB MSB | 2932 // LSB MSB |
| 2954 // | | | 2933 // | | |
| 2955 0x0000, // 0x00 - 0x0f 0000000000000000 | 2934 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 2956 0x0000, // 0x10 - 0x1f 0000000000000000 | 2935 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 2957 // + -. | 2936 // + -. |
| 2958 0x6800, // 0x20 - 0x2f 0000000000010110 | 2937 0x6800, // 0x20 - 0x2f 0000000000010110 |
| 2959 // 0123456789 | 2938 // 0123456789 |
| 2960 0x03ff, // 0x30 - 0x3f 1111111111000000 | 2939 0x03ff, // 0x30 - 0x3f 1111111111000000 |
| 2961 // | 2940 // |
| 2962 0x0000, // 0x40 - 0x4f 0111111111111111 | 2941 0x0000, // 0x40 - 0x4f 0111111111111111 |
| 2963 // | 2942 // |
| 2964 0x0000, // 0x50 - 0x5f 1111111111100001 | 2943 0x0000, // 0x50 - 0x5f 1111111111100001 |
| 2965 // abcdefghijklmno | 2944 // abcdefghijklmno |
| 2966 0xfffe, // 0x60 - 0x6f 0111111111111111 | 2945 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 2967 // pqrstuvwxyz | 2946 // pqrstuvwxyz |
| 2968 0x07ff]; // 0x70 - 0x7f 1111111111100010 | 2947 0x07ff, // 0x70 - 0x7f 1111111111100010 |
| 2948 ]; |
| 2969 | 2949 |
| 2970 // Sub delimiter characters combined with unreserved as of 3986. | 2950 // Sub delimiter characters combined with unreserved as of 3986. |
| 2971 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" | 2951 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" |
| 2972 // / "*" / "+" / "," / ";" / "=" | 2952 // / "*" / "+" / "," / ";" / "=" |
| 2973 // RFC 3986 section 2.3. | 2953 // RFC 3986 section 2.3. |
| 2974 // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | 2954 // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" |
| 2975 static const _subDelimitersTable = const [ | 2955 static const _subDelimitersTable = const [ |
| 2976 // LSB MSB | 2956 // LSB MSB |
| 2977 // | | | 2957 // | | |
| 2978 0x0000, // 0x00 - 0x0f 0000000000000000 | 2958 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 2979 0x0000, // 0x10 - 0x1f 0000000000000000 | 2959 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 2980 // ! $ &'()*+,-. | 2960 // ! $ &'()*+,-. |
| 2981 0x7fd2, // 0x20 - 0x2f 0100101111111110 | 2961 0x7fd2, // 0x20 - 0x2f 0100101111111110 |
| 2982 // 0123456789 ; = | 2962 // 0123456789 ; = |
| 2983 0x2bff, // 0x30 - 0x3f 1111111111010100 | 2963 0x2bff, // 0x30 - 0x3f 1111111111010100 |
| 2984 // ABCDEFGHIJKLMNO | 2964 // ABCDEFGHIJKLMNO |
| 2985 0xfffe, // 0x40 - 0x4f 0111111111111111 | 2965 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| 2986 // PQRSTUVWXYZ _ | 2966 // PQRSTUVWXYZ _ |
| 2987 0x87ff, // 0x50 - 0x5f 1111111111100001 | 2967 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 2988 // abcdefghijklmno | 2968 // abcdefghijklmno |
| 2989 0xfffe, // 0x60 - 0x6f 0111111111111111 | 2969 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 2990 // pqrstuvwxyz ~ | 2970 // pqrstuvwxyz ~ |
| 2991 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 2971 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 2972 ]; |
| 2992 | 2973 |
| 2993 // General delimiter characters, RFC 3986 section 2.2. | 2974 // General delimiter characters, RFC 3986 section 2.2. |
| 2994 // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" | 2975 // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" |
| 2995 // | 2976 // |
| 2996 static const _genDelimitersTable = const [ | 2977 static const _genDelimitersTable = const [ |
| 2997 // LSB MSB | 2978 // LSB MSB |
| 2998 // | | | 2979 // | | |
| 2999 0x0000, // 0x00 - 0x0f 0000000000000000 | 2980 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 3000 0x0000, // 0x10 - 0x1f 0000000000000000 | 2981 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 3001 // # / | 2982 // # / |
| 3002 0x8008, // 0x20 - 0x2f 0001000000000001 | 2983 0x8008, // 0x20 - 0x2f 0001000000000001 |
| 3003 // : ? | 2984 // : ? |
| 3004 0x8400, // 0x30 - 0x3f 0000000000100001 | 2985 0x8400, // 0x30 - 0x3f 0000000000100001 |
| 3005 // @ | 2986 // @ |
| 3006 0x0001, // 0x40 - 0x4f 1000000000000000 | 2987 0x0001, // 0x40 - 0x4f 1000000000000000 |
| 3007 // [ ] | 2988 // [ ] |
| 3008 0x2800, // 0x50 - 0x5f 0000000000010100 | 2989 0x2800, // 0x50 - 0x5f 0000000000010100 |
| 3009 // | 2990 // |
| 3010 0x0000, // 0x60 - 0x6f 0000000000000000 | 2991 0x0000, // 0x60 - 0x6f 0000000000000000 |
| 3011 // | 2992 // |
| 3012 0x0000]; // 0x70 - 0x7f 0000000000000000 | 2993 0x0000, // 0x70 - 0x7f 0000000000000000 |
| 2994 ]; |
| 3013 | 2995 |
| 3014 // Characters allowed in the userinfo as of RFC 3986. | 2996 // Characters allowed in the userinfo as of RFC 3986. |
| 3015 // RFC 3986 Apendix A | 2997 // RFC 3986 Apendix A |
| 3016 // userinfo = *( unreserved / pct-encoded / sub-delims / ':') | 2998 // userinfo = *( unreserved / pct-encoded / sub-delims / ':') |
| 3017 static const _userinfoTable = const [ | 2999 static const _userinfoTable = const [ |
| 3018 // LSB MSB | 3000 // LSB MSB |
| 3019 // | | | 3001 // | | |
| 3020 0x0000, // 0x00 - 0x0f 0000000000000000 | 3002 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 3021 0x0000, // 0x10 - 0x1f 0000000000000000 | 3003 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 3022 // ! $ &'()*+,-. | 3004 // ! $ &'()*+,-. |
| 3023 0x7fd2, // 0x20 - 0x2f 0100101111111110 | 3005 0x7fd2, // 0x20 - 0x2f 0100101111111110 |
| 3024 // 0123456789:; = | 3006 // 0123456789:; = |
| 3025 0x2fff, // 0x30 - 0x3f 1111111111110100 | 3007 0x2fff, // 0x30 - 0x3f 1111111111110100 |
| 3026 // ABCDEFGHIJKLMNO | 3008 // ABCDEFGHIJKLMNO |
| 3027 0xfffe, // 0x40 - 0x4f 0111111111111111 | 3009 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| 3028 // PQRSTUVWXYZ _ | 3010 // PQRSTUVWXYZ _ |
| 3029 0x87ff, // 0x50 - 0x5f 1111111111100001 | 3011 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 3030 // abcdefghijklmno | 3012 // abcdefghijklmno |
| 3031 0xfffe, // 0x60 - 0x6f 0111111111111111 | 3013 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 3032 // pqrstuvwxyz ~ | 3014 // pqrstuvwxyz ~ |
| 3033 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 3015 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 3016 ]; |
| 3034 | 3017 |
| 3035 // Characters allowed in the reg-name as of RFC 3986. | 3018 // Characters allowed in the reg-name as of RFC 3986. |
| 3036 // RFC 3986 Apendix A | 3019 // RFC 3986 Apendix A |
| 3037 // reg-name = *( unreserved / pct-encoded / sub-delims ) | 3020 // reg-name = *( unreserved / pct-encoded / sub-delims ) |
| 3038 static const _regNameTable = const [ | 3021 static const _regNameTable = const [ |
| 3039 // LSB MSB | 3022 // LSB MSB |
| 3040 // | | | 3023 // | | |
| 3041 0x0000, // 0x00 - 0x0f 0000000000000000 | 3024 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 3042 0x0000, // 0x10 - 0x1f 0000000000000000 | 3025 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 3043 // ! $%&'()*+,-. | 3026 // ! $%&'()*+,-. |
| 3044 0x7ff2, // 0x20 - 0x2f 0100111111111110 | 3027 0x7ff2, // 0x20 - 0x2f 0100111111111110 |
| 3045 // 0123456789 ; = | 3028 // 0123456789 ; = |
| 3046 0x2bff, // 0x30 - 0x3f 1111111111010100 | 3029 0x2bff, // 0x30 - 0x3f 1111111111010100 |
| 3047 // ABCDEFGHIJKLMNO | 3030 // ABCDEFGHIJKLMNO |
| 3048 0xfffe, // 0x40 - 0x4f 0111111111111111 | 3031 0xfffe, // 0x40 - 0x4f 0111111111111111 |
| 3049 // PQRSTUVWXYZ _ | 3032 // PQRSTUVWXYZ _ |
| 3050 0x87ff, // 0x50 - 0x5f 1111111111100001 | 3033 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 3051 // abcdefghijklmno | 3034 // abcdefghijklmno |
| 3052 0xfffe, // 0x60 - 0x6f 0111111111111111 | 3035 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 3053 // pqrstuvwxyz ~ | 3036 // pqrstuvwxyz ~ |
| 3054 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 3037 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 3038 ]; |
| 3055 | 3039 |
| 3056 // Characters allowed in the path as of RFC 3986. | 3040 // Characters allowed in the path as of RFC 3986. |
| 3057 // RFC 3986 section 3.3. | 3041 // RFC 3986 section 3.3. |
| 3058 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | 3042 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
| 3059 static const _pathCharTable = const [ | 3043 static const _pathCharTable = const [ |
| 3060 // LSB MSB | 3044 // LSB MSB |
| 3061 // | | | 3045 // | | |
| 3062 0x0000, // 0x00 - 0x0f 0000000000000000 | 3046 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 3063 0x0000, // 0x10 - 0x1f 0000000000000000 | 3047 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 3064 // ! $ &'()*+,-. | 3048 // ! $ &'()*+,-. |
| 3065 0x7fd2, // 0x20 - 0x2f 0100101111111110 | 3049 0x7fd2, // 0x20 - 0x2f 0100101111111110 |
| 3066 // 0123456789:; = | 3050 // 0123456789:; = |
| 3067 0x2fff, // 0x30 - 0x3f 1111111111110100 | 3051 0x2fff, // 0x30 - 0x3f 1111111111110100 |
| 3068 // @ABCDEFGHIJKLMNO | 3052 // @ABCDEFGHIJKLMNO |
| 3069 0xffff, // 0x40 - 0x4f 1111111111111111 | 3053 0xffff, // 0x40 - 0x4f 1111111111111111 |
| 3070 // PQRSTUVWXYZ _ | 3054 // PQRSTUVWXYZ _ |
| 3071 0x87ff, // 0x50 - 0x5f 1111111111100001 | 3055 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 3072 // abcdefghijklmno | 3056 // abcdefghijklmno |
| 3073 0xfffe, // 0x60 - 0x6f 0111111111111111 | 3057 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 3074 // pqrstuvwxyz ~ | 3058 // pqrstuvwxyz ~ |
| 3075 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 3059 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 3060 ]; |
| 3076 | 3061 |
| 3077 // Characters allowed in the path as of RFC 3986. | 3062 // Characters allowed in the path as of RFC 3986. |
| 3078 // RFC 3986 section 3.3 *and* slash. | 3063 // RFC 3986 section 3.3 *and* slash. |
| 3079 static const _pathCharOrSlashTable = const [ | 3064 static const _pathCharOrSlashTable = const [ |
| 3080 // LSB MSB | 3065 // LSB MSB |
| 3081 // | | | 3066 // | | |
| 3082 0x0000, // 0x00 - 0x0f 0000000000000000 | 3067 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 3083 0x0000, // 0x10 - 0x1f 0000000000000000 | 3068 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 3084 // ! $ &'()*+,-./ | 3069 // ! $ &'()*+,-./ |
| 3085 0xffd2, // 0x20 - 0x2f 0100101111111111 | 3070 0xffd2, // 0x20 - 0x2f 0100101111111111 |
| 3086 // 0123456789:; = | 3071 // 0123456789:; = |
| 3087 0x2fff, // 0x30 - 0x3f 1111111111110100 | 3072 0x2fff, // 0x30 - 0x3f 1111111111110100 |
| 3088 // @ABCDEFGHIJKLMNO | 3073 // @ABCDEFGHIJKLMNO |
| 3089 0xffff, // 0x40 - 0x4f 1111111111111111 | 3074 0xffff, // 0x40 - 0x4f 1111111111111111 |
| 3090 | 3075 |
| 3091 // PQRSTUVWXYZ _ | 3076 // PQRSTUVWXYZ _ |
| 3092 0x87ff, // 0x50 - 0x5f 1111111111100001 | 3077 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 3093 // abcdefghijklmno | 3078 // abcdefghijklmno |
| 3094 0xfffe, // 0x60 - 0x6f 0111111111111111 | 3079 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 3095 // pqrstuvwxyz ~ | 3080 // pqrstuvwxyz ~ |
| 3096 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 3081 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 3082 ]; |
| 3097 | 3083 |
| 3098 // Characters allowed in the query as of RFC 3986. | 3084 // Characters allowed in the query as of RFC 3986. |
| 3099 // RFC 3986 section 3.4. | 3085 // RFC 3986 section 3.4. |
| 3100 // query = *( pchar / "/" / "?" ) | 3086 // query = *( pchar / "/" / "?" ) |
| 3101 static const _queryCharTable = const [ | 3087 static const _queryCharTable = const [ |
| 3102 // LSB MSB | 3088 // LSB MSB |
| 3103 // | | | 3089 // | | |
| 3104 0x0000, // 0x00 - 0x0f 0000000000000000 | 3090 0x0000, // 0x00 - 0x0f 0000000000000000 |
| 3105 0x0000, // 0x10 - 0x1f 0000000000000000 | 3091 0x0000, // 0x10 - 0x1f 0000000000000000 |
| 3106 // ! $ &'()*+,-./ | 3092 // ! $ &'()*+,-./ |
| 3107 0xffd2, // 0x20 - 0x2f 0100101111111111 | 3093 0xffd2, // 0x20 - 0x2f 0100101111111111 |
| 3108 // 0123456789:; = ? | 3094 // 0123456789:; = ? |
| 3109 0xafff, // 0x30 - 0x3f 1111111111110101 | 3095 0xafff, // 0x30 - 0x3f 1111111111110101 |
| 3110 // @ABCDEFGHIJKLMNO | 3096 // @ABCDEFGHIJKLMNO |
| 3111 0xffff, // 0x40 - 0x4f 1111111111111111 | 3097 0xffff, // 0x40 - 0x4f 1111111111111111 |
| 3112 // PQRSTUVWXYZ _ | 3098 // PQRSTUVWXYZ _ |
| 3113 0x87ff, // 0x50 - 0x5f 1111111111100001 | 3099 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 3114 // abcdefghijklmno | 3100 // abcdefghijklmno |
| 3115 0xfffe, // 0x60 - 0x6f 0111111111111111 | 3101 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 3116 // pqrstuvwxyz ~ | 3102 // pqrstuvwxyz ~ |
| 3117 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 3103 0x47ff, // 0x70 - 0x7f 1111111111100010 |
| 3118 | 3104 ]; |
| 3119 } | 3105 } |
| 3120 | 3106 |
| 3121 // -------------------------------------------------------------------- | 3107 // -------------------------------------------------------------------- |
| 3122 // Data URI | 3108 // Data URI |
| 3123 // -------------------------------------------------------------------- | 3109 // -------------------------------------------------------------------- |
| 3124 | 3110 |
| 3125 /** | 3111 /** |
| 3126 * A way to access the structure of a `data:` URI. | 3112 * A way to access the structure of a `data:` URI. |
| 3127 * | 3113 * |
| 3128 * Data URIs are non-hierarchical URIs that can contain any binary data. | 3114 * Data URIs are non-hierarchical URIs that can contain any binary data. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3172 | 3158 |
| 3173 UriData._(this._text, this._separatorIndices, this._uriCache); | 3159 UriData._(this._text, this._separatorIndices, this._uriCache); |
| 3174 | 3160 |
| 3175 /** | 3161 /** |
| 3176 * Creates a `data:` URI containing the [content] string. | 3162 * Creates a `data:` URI containing the [content] string. |
| 3177 * | 3163 * |
| 3178 * Equivalent to `new Uri.dataFromString(...).data`, but may | 3164 * Equivalent to `new Uri.dataFromString(...).data`, but may |
| 3179 * be more efficient if the [uri] itself isn't used. | 3165 * be more efficient if the [uri] itself isn't used. |
| 3180 */ | 3166 */ |
| 3181 factory UriData.fromString(String content, | 3167 factory UriData.fromString(String content, |
| 3182 {String mimeType, | 3168 {String mimeType, |
| 3183 Encoding encoding, | 3169 Encoding encoding, |
| 3184 Map<String, String> parameters, | 3170 Map<String, String> parameters, |
| 3185 bool base64: false}) { | 3171 bool base64: false}) { |
| 3186 StringBuffer buffer = new StringBuffer(); | 3172 StringBuffer buffer = new StringBuffer(); |
| 3187 List<int> indices = [_noScheme]; | 3173 List<int> indices = [_noScheme]; |
| 3188 String charsetName; | 3174 String charsetName; |
| 3189 String encodingName; | 3175 String encodingName; |
| 3190 if (parameters != null) charsetName = parameters["charset"]; | 3176 if (parameters != null) charsetName = parameters["charset"]; |
| 3191 if (encoding == null) { | 3177 if (encoding == null) { |
| 3192 if (charsetName != null) { | 3178 if (charsetName != null) { |
| 3193 encoding = Encoding.getByName(charsetName); | 3179 encoding = Encoding.getByName(charsetName); |
| 3194 } | 3180 } |
| 3195 } else if (charsetName == null) { | 3181 } else if (charsetName == null) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 3210 return new UriData._(buffer.toString(), indices, null); | 3196 return new UriData._(buffer.toString(), indices, null); |
| 3211 } | 3197 } |
| 3212 | 3198 |
| 3213 /** | 3199 /** |
| 3214 * Creates a `data:` URI containing an encoding of [bytes]. | 3200 * Creates a `data:` URI containing an encoding of [bytes]. |
| 3215 * | 3201 * |
| 3216 * Equivalent to `new Uri.dataFromBytes(...).data`, but may | 3202 * Equivalent to `new Uri.dataFromBytes(...).data`, but may |
| 3217 * be more efficient if the [uri] itself isn't used. | 3203 * be more efficient if the [uri] itself isn't used. |
| 3218 */ | 3204 */ |
| 3219 factory UriData.fromBytes(List<int> bytes, | 3205 factory UriData.fromBytes(List<int> bytes, |
| 3220 {mimeType: "application/octet-stream", | 3206 {mimeType: "application/octet-stream", |
| 3221 Map<String, String> parameters, | 3207 Map<String, String> parameters, |
| 3222 percentEncoded: false}) { | 3208 percentEncoded: false}) { |
| 3223 StringBuffer buffer = new StringBuffer(); | 3209 StringBuffer buffer = new StringBuffer(); |
| 3224 List<int> indices = [_noScheme]; | 3210 List<int> indices = [_noScheme]; |
| 3225 _writeUri(mimeType, null, parameters, buffer, indices); | 3211 _writeUri(mimeType, null, parameters, buffer, indices); |
| 3226 indices.add(buffer.length); | 3212 indices.add(buffer.length); |
| 3227 if (percentEncoded) { | 3213 if (percentEncoded) { |
| 3228 buffer.write(','); | 3214 buffer.write(','); |
| 3229 _uriEncodeBytes(_uricTable, bytes, buffer); | 3215 _uriEncodeBytes(_uricTable, bytes, buffer); |
| 3230 } else { | 3216 } else { |
| 3231 buffer.write(';base64,'); | 3217 buffer.write(';base64,'); |
| 3232 indices.add(buffer.length - 1); | 3218 indices.add(buffer.length - 1); |
| 3233 BASE64.encoder | 3219 BASE64.encoder |
| 3234 .startChunkedConversion( | 3220 .startChunkedConversion( |
| 3235 new StringConversionSink.fromStringSink(buffer)) | 3221 new StringConversionSink.fromStringSink(buffer)) |
| 3236 .addSlice(bytes, 0, bytes.length, true); | 3222 .addSlice(bytes, 0, bytes.length, true); |
| 3237 } | 3223 } |
| 3238 | 3224 |
| 3239 return new UriData._(buffer.toString(), indices, null); | 3225 return new UriData._(buffer.toString(), indices, null); |
| 3240 } | 3226 } |
| 3241 | 3227 |
| 3242 /** | 3228 /** |
| 3243 * Creates a `DataUri` from a [Uri] which must have `data` as [Uri.scheme]. | 3229 * Creates a `DataUri` from a [Uri] which must have `data` as [Uri.scheme]. |
| 3244 * | 3230 * |
| 3245 * The [uri] must have scheme `data` and no authority or fragment, | 3231 * The [uri] must have scheme `data` and no authority or fragment, |
| 3246 * and the path (concatenated with the query, if there is one) must be valid | 3232 * and the path (concatenated with the query, if there is one) must be valid |
| 3247 * as data URI content with the same rules as [parse]. | 3233 * as data URI content with the same rules as [parse]. |
| 3248 */ | 3234 */ |
| 3249 factory UriData.fromUri(Uri uri) { | 3235 factory UriData.fromUri(Uri uri) { |
| 3250 if (uri.scheme != "data") { | 3236 if (uri.scheme != "data") { |
| 3251 throw new ArgumentError.value(uri, "uri", | 3237 throw new ArgumentError.value(uri, "uri", "Scheme must be 'data'"); |
| 3252 "Scheme must be 'data'"); | |
| 3253 } | 3238 } |
| 3254 if (uri.hasAuthority) { | 3239 if (uri.hasAuthority) { |
| 3255 throw new ArgumentError.value(uri, "uri", | 3240 throw new ArgumentError.value( |
| 3256 "Data uri must not have authority"); | 3241 uri, "uri", "Data uri must not have authority"); |
| 3257 } | 3242 } |
| 3258 if (uri.hasFragment) { | 3243 if (uri.hasFragment) { |
| 3259 throw new ArgumentError.value(uri, "uri", | 3244 throw new ArgumentError.value( |
| 3260 "Data uri must not have a fragment part"); | 3245 uri, "uri", "Data uri must not have a fragment part"); |
| 3261 } | 3246 } |
| 3262 if (!uri.hasQuery) { | 3247 if (!uri.hasQuery) { |
| 3263 return _parse(uri.path, 0, uri); | 3248 return _parse(uri.path, 0, uri); |
| 3264 } | 3249 } |
| 3265 // Includes path and query (and leading "data:"). | 3250 // Includes path and query (and leading "data:"). |
| 3266 return _parse("$uri", 5, uri); | 3251 return _parse("$uri", 5, uri); |
| 3267 } | 3252 } |
| 3268 | 3253 |
| 3269 /** | 3254 /** |
| 3270 * Writes the initial part of a `data:` uri, from after the "data:" | 3255 * Writes the initial part of a `data:` uri, from after the "data:" |
| 3271 * until just before the ',' before the data, or before a `;base64,` | 3256 * until just before the ',' before the data, or before a `;base64,` |
| 3272 * marker. | 3257 * marker. |
| 3273 * | 3258 * |
| 3274 * Of an [indices] list is passed, separator indices are stored in that | 3259 * Of an [indices] list is passed, separator indices are stored in that |
| 3275 * list. | 3260 * list. |
| 3276 */ | 3261 */ |
| 3277 static void _writeUri(String mimeType, | 3262 static void _writeUri(String mimeType, String charsetName, |
| 3278 String charsetName, | 3263 Map<String, String> parameters, StringBuffer buffer, List indices) { |
| 3279 Map<String, String> parameters, | |
| 3280 StringBuffer buffer, List indices) { | |
| 3281 if (mimeType == null || mimeType == "text/plain") { | 3264 if (mimeType == null || mimeType == "text/plain") { |
| 3282 mimeType = ""; | 3265 mimeType = ""; |
| 3283 } | 3266 } |
| 3284 if (mimeType.isEmpty || identical(mimeType, "application/octet-stream")) { | 3267 if (mimeType.isEmpty || identical(mimeType, "application/octet-stream")) { |
| 3285 buffer.write(mimeType); // Common cases need no escaping. | 3268 buffer.write(mimeType); // Common cases need no escaping. |
| 3286 } else { | 3269 } else { |
| 3287 int slashIndex = _validateMimeType(mimeType); | 3270 int slashIndex = _validateMimeType(mimeType); |
| 3288 if (slashIndex < 0) { | 3271 if (slashIndex < 0) { |
| 3289 throw new ArgumentError.value(mimeType, "mimeType", | 3272 throw new ArgumentError.value( |
| 3290 "Invalid MIME type"); | 3273 mimeType, "mimeType", "Invalid MIME type"); |
| 3291 } | 3274 } |
| 3292 buffer.write(_Uri._uriEncode(_tokenCharTable, | 3275 buffer.write(_Uri._uriEncode( |
| 3293 mimeType.substring(0, slashIndex), | 3276 _tokenCharTable, mimeType.substring(0, slashIndex), UTF8, false)); |
| 3294 UTF8, false)); | |
| 3295 buffer.write("/"); | 3277 buffer.write("/"); |
| 3296 buffer.write(_Uri._uriEncode(_tokenCharTable, | 3278 buffer.write(_Uri._uriEncode( |
| 3297 mimeType.substring(slashIndex + 1), | 3279 _tokenCharTable, mimeType.substring(slashIndex + 1), UTF8, false)); |
| 3298 UTF8, false)); | |
| 3299 } | 3280 } |
| 3300 if (charsetName != null) { | 3281 if (charsetName != null) { |
| 3301 if (indices != null) { | 3282 if (indices != null) { |
| 3302 indices..add(buffer.length) | 3283 indices..add(buffer.length)..add(buffer.length + 8); |
| 3303 ..add(buffer.length + 8); | |
| 3304 } | 3284 } |
| 3305 buffer.write(";charset="); | 3285 buffer.write(";charset="); |
| 3306 buffer.write(_Uri._uriEncode(_tokenCharTable, charsetName, UTF8, false)); | 3286 buffer.write(_Uri._uriEncode(_tokenCharTable, charsetName, UTF8, false)); |
| 3307 } | 3287 } |
| 3308 parameters?.forEach((var key, var value) { | 3288 parameters?.forEach((var key, var value) { |
| 3309 if (key.isEmpty) { | 3289 if (key.isEmpty) { |
| 3310 throw new ArgumentError.value("", "Parameter names must not be empty"); | 3290 throw new ArgumentError.value("", "Parameter names must not be empty"); |
| 3311 } | 3291 } |
| 3312 if (value.isEmpty) { | 3292 if (value.isEmpty) { |
| 3313 throw new ArgumentError.value("", "Parameter values must not be empty", | 3293 throw new ArgumentError.value( |
| 3314 'parameters["$key"]'); | 3294 "", "Parameter values must not be empty", 'parameters["$key"]'); |
| 3315 } | 3295 } |
| 3316 if (indices != null) indices.add(buffer.length); | 3296 if (indices != null) indices.add(buffer.length); |
| 3317 buffer.write(';'); | 3297 buffer.write(';'); |
| 3318 // Encode any non-RFC2045-token character and both '%' and '#'. | 3298 // Encode any non-RFC2045-token character and both '%' and '#'. |
| 3319 buffer.write(_Uri._uriEncode(_tokenCharTable, key, UTF8, false)); | 3299 buffer.write(_Uri._uriEncode(_tokenCharTable, key, UTF8, false)); |
| 3320 if (indices != null) indices.add(buffer.length); | 3300 if (indices != null) indices.add(buffer.length); |
| 3321 buffer.write('='); | 3301 buffer.write('='); |
| 3322 buffer.write(_Uri._uriEncode(_tokenCharTable, value, UTF8, false)); | 3302 buffer.write(_Uri._uriEncode(_tokenCharTable, value, UTF8, false)); |
| 3323 }); | 3303 }); |
| 3324 } | 3304 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3395 * as path. | 3375 * as path. |
| 3396 */ | 3376 */ |
| 3397 Uri get uri { | 3377 Uri get uri { |
| 3398 if (_uriCache != null) return _uriCache; | 3378 if (_uriCache != null) return _uriCache; |
| 3399 String path = _text; | 3379 String path = _text; |
| 3400 String query = null; | 3380 String query = null; |
| 3401 int colonIndex = _separatorIndices[0]; | 3381 int colonIndex = _separatorIndices[0]; |
| 3402 int queryIndex = _text.indexOf('?', colonIndex + 1); | 3382 int queryIndex = _text.indexOf('?', colonIndex + 1); |
| 3403 int end = _text.length; | 3383 int end = _text.length; |
| 3404 if (queryIndex >= 0) { | 3384 if (queryIndex >= 0) { |
| 3405 query = _Uri._normalizeOrSubstring(_text, queryIndex + 1, end, _Uri._query
CharTable); | 3385 query = _Uri._normalizeOrSubstring( |
| 3386 _text, queryIndex + 1, end, _Uri._queryCharTable); |
| 3406 end = queryIndex; | 3387 end = queryIndex; |
| 3407 } | 3388 } |
| 3408 path = _Uri._normalizeOrSubstring(_text, colonIndex + 1, end, | 3389 path = _Uri._normalizeOrSubstring( |
| 3409 _Uri._pathCharOrSlashTable); | 3390 _text, colonIndex + 1, end, _Uri._pathCharOrSlashTable); |
| 3410 _uriCache = new _DataUri(this, path, query); | 3391 _uriCache = new _DataUri(this, path, query); |
| 3411 return _uriCache; | 3392 return _uriCache; |
| 3412 } | 3393 } |
| 3413 | 3394 |
| 3414 /** | 3395 /** |
| 3415 * The MIME type of the data URI. | 3396 * The MIME type of the data URI. |
| 3416 * | 3397 * |
| 3417 * A data URI consists of a "media type" followed by data. | 3398 * A data URI consists of a "media type" followed by data. |
| 3418 * The media type starts with a MIME type and can be followed by | 3399 * The media type starts with a MIME type and can be followed by |
| 3419 * extra parameters. | 3400 * extra parameters. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 3444 * If the parameters of the media type contains a `charset` parameter | 3425 * If the parameters of the media type contains a `charset` parameter |
| 3445 * then this returns its value, otherwise it returns `US-ASCII`, | 3426 * then this returns its value, otherwise it returns `US-ASCII`, |
| 3446 * which is the default charset for data URIs. | 3427 * which is the default charset for data URIs. |
| 3447 * If the value contain non-ASCII percent escapes, they are decoded as UTF-8. | 3428 * If the value contain non-ASCII percent escapes, they are decoded as UTF-8. |
| 3448 * | 3429 * |
| 3449 * If the MIME type representation in the URI text contains URI escapes, | 3430 * If the MIME type representation in the URI text contains URI escapes, |
| 3450 * they are unescaped in the returned string. | 3431 * they are unescaped in the returned string. |
| 3451 */ | 3432 */ |
| 3452 String get charset { | 3433 String get charset { |
| 3453 int parameterStart = 1; | 3434 int parameterStart = 1; |
| 3454 int parameterEnd = _separatorIndices.length - 1; // The ',' before data. | 3435 int parameterEnd = _separatorIndices.length - 1; // The ',' before data. |
| 3455 if (isBase64) { | 3436 if (isBase64) { |
| 3456 // There is a ";base64" separator, so subtract one for that as well. | 3437 // There is a ";base64" separator, so subtract one for that as well. |
| 3457 parameterEnd -= 1; | 3438 parameterEnd -= 1; |
| 3458 } | 3439 } |
| 3459 for (int i = parameterStart; i < parameterEnd; i += 2) { | 3440 for (int i = parameterStart; i < parameterEnd; i += 2) { |
| 3460 var keyStart = _separatorIndices[i] + 1; | 3441 var keyStart = _separatorIndices[i] + 1; |
| 3461 var keyEnd = _separatorIndices[i + 1]; | 3442 var keyEnd = _separatorIndices[i + 1]; |
| 3462 if (keyEnd == keyStart + 7 && _text.startsWith("charset", keyStart)) { | 3443 if (keyEnd == keyStart + 7 && _text.startsWith("charset", keyStart)) { |
| 3463 return _Uri._uriDecode(_text, keyEnd + 1, _separatorIndices[i + 2], | 3444 return _Uri._uriDecode( |
| 3464 UTF8, false); | 3445 _text, keyEnd + 1, _separatorIndices[i + 2], UTF8, false); |
| 3465 } | 3446 } |
| 3466 } | 3447 } |
| 3467 return "US-ASCII"; | 3448 return "US-ASCII"; |
| 3468 } | 3449 } |
| 3469 | 3450 |
| 3470 /** | 3451 /** |
| 3471 * Whether the data is Base64 encoded or not. | 3452 * Whether the data is Base64 encoded or not. |
| 3472 */ | 3453 */ |
| 3473 bool get isBase64 => _separatorIndices.length.isOdd; | 3454 bool get isBase64 => _separatorIndices.length.isOdd; |
| 3474 | 3455 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3542 * if it is recognized by [Encoding.getByName], otherwise it defaults to | 3523 * if it is recognized by [Encoding.getByName], otherwise it defaults to |
| 3543 * the [ASCII] encoding, which is the default encoding for data URIs | 3524 * the [ASCII] encoding, which is the default encoding for data URIs |
| 3544 * that do not specify an encoding. | 3525 * that do not specify an encoding. |
| 3545 * | 3526 * |
| 3546 * If the content is not Base64 encoded, it will first have percent-escapes | 3527 * If the content is not Base64 encoded, it will first have percent-escapes |
| 3547 * converted to bytes and then the character codes and byte values are | 3528 * converted to bytes and then the character codes and byte values are |
| 3548 * decoded using [encoding]. | 3529 * decoded using [encoding]. |
| 3549 */ | 3530 */ |
| 3550 String contentAsString({Encoding encoding}) { | 3531 String contentAsString({Encoding encoding}) { |
| 3551 if (encoding == null) { | 3532 if (encoding == null) { |
| 3552 var charset = this.charset; // Returns "US-ASCII" if not present. | 3533 var charset = this.charset; // Returns "US-ASCII" if not present. |
| 3553 encoding = Encoding.getByName(charset); | 3534 encoding = Encoding.getByName(charset); |
| 3554 if (encoding == null) { | 3535 if (encoding == null) { |
| 3555 throw new UnsupportedError("Unknown charset: $charset"); | 3536 throw new UnsupportedError("Unknown charset: $charset"); |
| 3556 } | 3537 } |
| 3557 } | 3538 } |
| 3558 String text = _text; | 3539 String text = _text; |
| 3559 int start = _separatorIndices.last + 1; | 3540 int start = _separatorIndices.last + 1; |
| 3560 if (isBase64) { | 3541 if (isBase64) { |
| 3561 var converter = BASE64.decoder.fuse(encoding.decoder); | 3542 var converter = BASE64.decoder.fuse(encoding.decoder); |
| 3562 return converter.convert(text.substring(start)); | 3543 return converter.convert(text.substring(start)); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 3578 * If the values contain non-ASCII values or percent escapes, | 3559 * If the values contain non-ASCII values or percent escapes, |
| 3579 * they are decoded as UTF-8. | 3560 * they are decoded as UTF-8. |
| 3580 */ | 3561 */ |
| 3581 Map<String, String> get parameters { | 3562 Map<String, String> get parameters { |
| 3582 var result = <String, String>{}; | 3563 var result = <String, String>{}; |
| 3583 for (int i = 3; i < _separatorIndices.length; i += 2) { | 3564 for (int i = 3; i < _separatorIndices.length; i += 2) { |
| 3584 var start = _separatorIndices[i - 2] + 1; | 3565 var start = _separatorIndices[i - 2] + 1; |
| 3585 var equals = _separatorIndices[i - 1]; | 3566 var equals = _separatorIndices[i - 1]; |
| 3586 var end = _separatorIndices[i]; | 3567 var end = _separatorIndices[i]; |
| 3587 String key = _Uri._uriDecode(_text, start, equals, UTF8, false); | 3568 String key = _Uri._uriDecode(_text, start, equals, UTF8, false); |
| 3588 String value = _Uri._uriDecode(_text,equals + 1, end, UTF8, false); | 3569 String value = _Uri._uriDecode(_text, equals + 1, end, UTF8, false); |
| 3589 result[key] = value; | 3570 result[key] = value; |
| 3590 } | 3571 } |
| 3591 return result; | 3572 return result; |
| 3592 } | 3573 } |
| 3593 | 3574 |
| 3594 static UriData _parse(String text, int start, Uri sourceUri) { | 3575 static UriData _parse(String text, int start, Uri sourceUri) { |
| 3595 assert(start == 0 || start == 5); | 3576 assert(start == 0 || start == 5); |
| 3596 assert((start == 5) == text.startsWith("data:")); | 3577 assert((start == 5) == text.startsWith("data:")); |
| 3597 | 3578 |
| 3598 /// Character codes. | 3579 /// Character codes. |
| 3599 const int comma = 0x2c; | 3580 const int comma = 0x2c; |
| 3600 const int slash = 0x2f; | 3581 const int slash = 0x2f; |
| 3601 const int semicolon = 0x3b; | 3582 const int semicolon = 0x3b; |
| 3602 const int equals = 0x3d; | 3583 const int equals = 0x3d; |
| 3603 List<int> indices = [start - 1]; | 3584 List<int> indices = [start - 1]; |
| 3604 int slashIndex = -1; | 3585 int slashIndex = -1; |
| 3605 var char; | 3586 var char; |
| 3606 int i = start; | 3587 int i = start; |
| 3607 for (; i < text.length; i++) { | 3588 for (; i < text.length; i++) { |
| 3608 char = text.codeUnitAt(i); | 3589 char = text.codeUnitAt(i); |
| 3609 if (char == comma || char == semicolon) break; | 3590 if (char == comma || char == semicolon) break; |
| 3610 if (char == slash) { | 3591 if (char == slash) { |
| 3611 if (slashIndex < 0) { | 3592 if (slashIndex < 0) { |
| 3612 slashIndex = i; | 3593 slashIndex = i; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3648 } | 3629 } |
| 3649 indices.add(i); | 3630 indices.add(i); |
| 3650 bool isBase64 = indices.length.isOdd; | 3631 bool isBase64 = indices.length.isOdd; |
| 3651 if (isBase64) { | 3632 if (isBase64) { |
| 3652 text = BASE64.normalize(text, i + 1, text.length); | 3633 text = BASE64.normalize(text, i + 1, text.length); |
| 3653 } else { | 3634 } else { |
| 3654 // Validate "data" part, must only contain RFC 2396 'uric' characters | 3635 // Validate "data" part, must only contain RFC 2396 'uric' characters |
| 3655 // (reserved, unreserved, or escape sequences). | 3636 // (reserved, unreserved, or escape sequences). |
| 3656 // Normalize to this (throws on a fragment separator). | 3637 // Normalize to this (throws on a fragment separator). |
| 3657 var data = _Uri._normalize(text, i + 1, text.length, _uricTable, | 3638 var data = _Uri._normalize(text, i + 1, text.length, _uricTable, |
| 3658 escapeDelimiters: true); | 3639 escapeDelimiters: true); |
| 3659 if (data != null) { | 3640 if (data != null) { |
| 3660 text = text.replaceRange(i + 1, text.length, data); | 3641 text = text.replaceRange(i + 1, text.length, data); |
| 3661 } | 3642 } |
| 3662 } | 3643 } |
| 3663 return new UriData._(text, indices, sourceUri); | 3644 return new UriData._(text, indices, sourceUri); |
| 3664 } | 3645 } |
| 3665 | 3646 |
| 3666 /** | 3647 /** |
| 3667 * Like [Uri._uriEncode] but takes the input as bytes, not a string. | 3648 * Like [Uri._uriEncode] but takes the input as bytes, not a string. |
| 3668 * | 3649 * |
| 3669 * Encodes into [buffer] instead of creating its own buffer. | 3650 * Encodes into [buffer] instead of creating its own buffer. |
| 3670 */ | 3651 */ |
| 3671 static void _uriEncodeBytes(List<int> canonicalTable, | 3652 static void _uriEncodeBytes( |
| 3672 List<int> bytes, | 3653 List<int> canonicalTable, List<int> bytes, StringSink buffer) { |
| 3673 StringSink buffer) { | |
| 3674 // Encode the string into bytes then generate an ASCII only string | 3654 // Encode the string into bytes then generate an ASCII only string |
| 3675 // by percent encoding selected bytes. | 3655 // by percent encoding selected bytes. |
| 3676 int byteOr = 0; | 3656 int byteOr = 0; |
| 3677 for (int i = 0; i < bytes.length; i++) { | 3657 for (int i = 0; i < bytes.length; i++) { |
| 3678 int byte = bytes[i]; | 3658 int byte = bytes[i]; |
| 3679 byteOr |= byte; | 3659 byteOr |= byte; |
| 3680 if (byte < 128 && | 3660 if (byte < 128 && |
| 3681 ((canonicalTable[byte >> 4] & (1 << (byte & 0x0f))) != 0)) { | 3661 ((canonicalTable[byte >> 4] & (1 << (byte & 0x0f))) != 0)) { |
| 3682 buffer.writeCharCode(byte); | 3662 buffer.writeCharCode(byte); |
| 3683 } else { | 3663 } else { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 3700 (_separatorIndices[0] == _noScheme) ? "data:$_text" : _text; | 3680 (_separatorIndices[0] == _noScheme) ? "data:$_text" : _text; |
| 3701 | 3681 |
| 3702 // Table of the `token` characters of RFC 2045 in a URI. | 3682 // Table of the `token` characters of RFC 2045 in a URI. |
| 3703 // | 3683 // |
| 3704 // A token is any US-ASCII character except SPACE, control characters and | 3684 // A token is any US-ASCII character except SPACE, control characters and |
| 3705 // `tspecial` characters. The `tspecial` category is: | 3685 // `tspecial` characters. The `tspecial` category is: |
| 3706 // '(', ')', '<', '>', '@', ',', ';', ':', '\', '"', '/', '[, ']', '?', '='. | 3686 // '(', ')', '<', '>', '@', ',', ';', ':', '\', '"', '/', '[, ']', '?', '='. |
| 3707 // | 3687 // |
| 3708 // In a data URI, we also need to escape '%' and '#' characters. | 3688 // In a data URI, we also need to escape '%' and '#' characters. |
| 3709 static const _tokenCharTable = const [ | 3689 static const _tokenCharTable = const [ |
| 3710 // LSB MSB | 3690 // LSB MSB |
| 3711 // | | | 3691 // | | |
| 3712 0x0000, // 0x00 - 0x0f 00000000 00000000 | 3692 0x0000, // 0x00 - 0x0f 00000000 00000000 |
| 3713 0x0000, // 0x10 - 0x1f 00000000 00000000 | 3693 0x0000, // 0x10 - 0x1f 00000000 00000000 |
| 3714 // ! $ &' *+ -. | 3694 // ! $ &' *+ -. |
| 3715 0x6cd2, // 0x20 - 0x2f 01001011 00110110 | 3695 0x6cd2, // 0x20 - 0x2f 01001011 00110110 |
| 3716 // 01234567 89 | 3696 // 01234567 89 |
| 3717 0x03ff, // 0x30 - 0x3f 11111111 11000000 | 3697 0x03ff, // 0x30 - 0x3f 11111111 11000000 |
| 3718 // ABCDEFG HIJKLMNO | 3698 // ABCDEFG HIJKLMNO |
| 3719 0xfffe, // 0x40 - 0x4f 01111111 11111111 | 3699 0xfffe, // 0x40 - 0x4f 01111111 11111111 |
| 3720 // PQRSTUVW XYZ ^_ | 3700 // PQRSTUVW XYZ ^_ |
| 3721 0xc7ff, // 0x50 - 0x5f 11111111 11100011 | 3701 0xc7ff, // 0x50 - 0x5f 11111111 11100011 |
| 3722 // `abcdefg hijklmno | 3702 // `abcdefg hijklmno |
| 3723 0xffff, // 0x60 - 0x6f 11111111 11111111 | 3703 0xffff, // 0x60 - 0x6f 11111111 11111111 |
| 3724 // pqrstuvw xyz{|}~ | 3704 // pqrstuvw xyz{|}~ |
| 3725 0x7fff]; // 0x70 - 0x7f 11111111 11111110 | 3705 0x7fff, // 0x70 - 0x7f 11111111 11111110 |
| 3706 ]; |
| 3726 | 3707 |
| 3727 // All non-escape RFC-2396 uric characters. | 3708 // All non-escape RFC-2396 uric characters. |
| 3728 // | 3709 // |
| 3729 // uric = reserved | unreserved | escaped | 3710 // uric = reserved | unreserved | escaped |
| 3730 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | 3711 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
| 3731 // unreserved = alphanum | mark | 3712 // unreserved = alphanum | mark |
| 3732 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" | 3713 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" |
| 3733 // | 3714 // |
| 3734 // This is the same characters as in a URI query (which is URI pchar plus '?') | 3715 // This is the same characters as in a URI query (which is URI pchar plus '?') |
| 3735 static const _uricTable = _Uri._queryCharTable; | 3716 static const _uricTable = _Uri._queryCharTable; |
| 3736 | 3717 |
| 3737 // Characters allowed in base-64 encoding (alphanumeric, '/', '+' and '='). | 3718 // Characters allowed in base-64 encoding (alphanumeric, '/', '+' and '='). |
| 3738 static const _base64Table = const [ | 3719 static const _base64Table = const [ |
| 3739 // LSB MSB | 3720 // LSB MSB |
| 3740 // | | | 3721 // | | |
| 3741 0x0000, // 0x00 - 0x0f 00000000 00000000 | 3722 0x0000, // 0x00 - 0x0f 00000000 00000000 |
| 3742 0x0000, // 0x10 - 0x1f 00000000 00000000 | 3723 0x0000, // 0x10 - 0x1f 00000000 00000000 |
| 3743 // + / | 3724 // + / |
| 3744 0x8800, // 0x20 - 0x2f 00000000 00010001 | 3725 0x8800, // 0x20 - 0x2f 00000000 00010001 |
| 3745 // 01234567 89 | 3726 // 01234567 89 |
| 3746 0x03ff, // 0x30 - 0x3f 11111111 11000000 | 3727 0x03ff, // 0x30 - 0x3f 11111111 11000000 |
| 3747 // ABCDEFG HIJKLMNO | 3728 // ABCDEFG HIJKLMNO |
| 3748 0xfffe, // 0x40 - 0x4f 01111111 11111111 | 3729 0xfffe, // 0x40 - 0x4f 01111111 11111111 |
| 3749 // PQRSTUVW XYZ | 3730 // PQRSTUVW XYZ |
| 3750 0x07ff, // 0x50 - 0x5f 11111111 11100000 | 3731 0x07ff, // 0x50 - 0x5f 11111111 11100000 |
| 3751 // abcdefg hijklmno | 3732 // abcdefg hijklmno |
| 3752 0xfffe, // 0x60 - 0x6f 01111111 11111111 | 3733 0xfffe, // 0x60 - 0x6f 01111111 11111111 |
| 3753 // pqrstuvw xyz | 3734 // pqrstuvw xyz |
| 3754 0x07ff, // 0x70 - 0x7f 11111111 11100000 | 3735 0x07ff, // 0x70 - 0x7f 11111111 11100000 |
| 3755 ]; | 3736 ]; |
| 3756 } | 3737 } |
| 3757 | 3738 |
| 3758 // -------------------------------------------------------------------- | 3739 // -------------------------------------------------------------------- |
| 3759 // Constants used to read the scanner result. | 3740 // Constants used to read the scanner result. |
| 3760 // The indices points into the table filled by [_scan] which contains | 3741 // The indices points into the table filled by [_scan] which contains |
| 3761 // recognized positions in the scanned URI. | 3742 // recognized positions in the scanned URI. |
| 3762 // The `0` index is only used internally. | 3743 // The `0` index is only used internally. |
| 3763 | 3744 |
| 3764 /// Index of the position of that `:` after a scheme. | 3745 /// Index of the position of that `:` after a scheme. |
| 3765 const int _schemeEndIndex = 1; | 3746 const int _schemeEndIndex = 1; |
| 3747 |
| 3766 /// Index of the position of the character just before the host name. | 3748 /// Index of the position of the character just before the host name. |
| 3767 const int _hostStartIndex = 2; | 3749 const int _hostStartIndex = 2; |
| 3750 |
| 3768 /// Index of the position of the `:` before a port value. | 3751 /// Index of the position of the `:` before a port value. |
| 3769 const int _portStartIndex = 3; | 3752 const int _portStartIndex = 3; |
| 3753 |
| 3770 /// Index of the position of the first character of a path. | 3754 /// Index of the position of the first character of a path. |
| 3771 const int _pathStartIndex = 4; | 3755 const int _pathStartIndex = 4; |
| 3756 |
| 3772 /// Index of the position of the `?` before a query. | 3757 /// Index of the position of the `?` before a query. |
| 3773 const int _queryStartIndex = 5; | 3758 const int _queryStartIndex = 5; |
| 3759 |
| 3774 /// Index of the position of the `#` before a fragment. | 3760 /// Index of the position of the `#` before a fragment. |
| 3775 const int _fragmentStartIndex = 6; | 3761 const int _fragmentStartIndex = 6; |
| 3762 |
| 3776 /// Index of a position where the URI was determined to be "non-simple". | 3763 /// Index of a position where the URI was determined to be "non-simple". |
| 3777 const int _notSimpleIndex = 7; | 3764 const int _notSimpleIndex = 7; |
| 3778 | 3765 |
| 3779 // Initial state for scanner. | 3766 // Initial state for scanner. |
| 3780 const int _uriStart = 00; | 3767 const int _uriStart = 00; |
| 3781 | 3768 |
| 3782 // If scanning of a URI terminates in this state or above, | 3769 // If scanning of a URI terminates in this state or above, |
| 3783 // consider the URI non-simple | 3770 // consider the URI non-simple |
| 3784 const int _nonSimpleEndStates = 14; | 3771 const int _nonSimpleEndStates = 14; |
| 3785 | 3772 |
| 3786 // Initial state for scheme validation. | 3773 // Initial state for scheme validation. |
| 3787 const int _schemeStart = 20; | 3774 const int _schemeStart = 20; |
| 3788 | 3775 |
| 3789 /// Transition tables used to scan a URI to determine its structure. | 3776 /// Transition tables used to scan a URI to determine its structure. |
| 3790 /// | 3777 /// |
| 3791 /// The tables represent a state machine with output. | 3778 /// The tables represent a state machine with output. |
| 3792 /// | 3779 /// |
| 3793 /// To scan the URI, start in the [_uriStart] state, then read each character | 3780 /// To scan the URI, start in the [_uriStart] state, then read each character |
| 3794 /// of the URI in order, from start to end, and for each character perform a | 3781 /// of the URI in order, from start to end, and for each character perform a |
| 3795 /// transition to a new state while writing the current position into the output | 3782 /// transition to a new state while writing the current position into the output |
| 3796 /// buffer at a designated index. | 3783 /// buffer at a designated index. |
| 3797 /// | 3784 /// |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3876 /// The transition tables cannot detect a trailing ".." in the path, | 3863 /// The transition tables cannot detect a trailing ".." in the path, |
| 3877 /// followed by a query or fragment, because the segment is not known to be | 3864 /// followed by a query or fragment, because the segment is not known to be |
| 3878 /// complete until we are past it, and we then need to store the query/fragment | 3865 /// complete until we are past it, and we then need to store the query/fragment |
| 3879 /// start instead. This cast is checked manually post-scanning (such a path | 3866 /// start instead. This cast is checked manually post-scanning (such a path |
| 3880 /// needs to be normalized to end in "../", so the URI shouldn't be considered | 3867 /// needs to be normalized to end in "../", so the URI shouldn't be considered |
| 3881 /// simple). | 3868 /// simple). |
| 3882 List<Uint8List> _createTables() { | 3869 List<Uint8List> _createTables() { |
| 3883 // TODO(lrn): Use a precomputed table. | 3870 // TODO(lrn): Use a precomputed table. |
| 3884 | 3871 |
| 3885 // Total number of states for the scanner. | 3872 // Total number of states for the scanner. |
| 3886 const int stateCount = 22; | 3873 const int stateCount = 22; |
| 3887 | 3874 |
| 3888 // States used to scan a URI from scratch. | 3875 // States used to scan a URI from scratch. |
| 3889 const int schemeOrPath = 01; | 3876 const int schemeOrPath = 01; |
| 3890 const int authOrPath = 02; | 3877 const int authOrPath = 02; |
| 3891 const int authOrPathSlash = 03; | 3878 const int authOrPathSlash = 03; |
| 3892 const int uinfoOrHost0 = 04; | 3879 const int uinfoOrHost0 = 04; |
| 3893 const int uinfoOrHost = 05; | 3880 const int uinfoOrHost = 05; |
| 3894 const int uinfoOrPort0 = 06; | 3881 const int uinfoOrPort0 = 06; |
| 3895 const int uinfoOrPort = 07; | 3882 const int uinfoOrPort = 07; |
| 3896 const int ipv6Host = 08; | 3883 const int ipv6Host = 08; |
| 3897 const int relPathSeg = 09; | 3884 const int relPathSeg = 09; |
| 3898 const int pathSeg = 10; | 3885 const int pathSeg = 10; |
| 3899 const int path = 11; | 3886 const int path = 11; |
| 3900 const int query = 12; | 3887 const int query = 12; |
| 3901 const int fragment = 13; | 3888 const int fragment = 13; |
| 3902 const int schemeOrPathDot = 14; | 3889 const int schemeOrPathDot = 14; |
| 3903 const int schemeOrPathDot2 = 15; | 3890 const int schemeOrPathDot2 = 15; |
| 3904 const int relPathSegDot = 16; | 3891 const int relPathSegDot = 16; |
| 3905 const int relPathSegDot2 = 17; | 3892 const int relPathSegDot2 = 17; |
| 3906 const int pathSegDot = 18; | 3893 const int pathSegDot = 18; |
| 3907 const int pathSegDot2 = 19; | 3894 const int pathSegDot2 = 19; |
| 3908 | 3895 |
| 3909 // States used to validate a scheme after its end position has been found. | 3896 // States used to validate a scheme after its end position has been found. |
| 3910 const int scheme0 = _schemeStart; | 3897 const int scheme0 = _schemeStart; |
| 3911 const int scheme = 21; | 3898 const int scheme = 21; |
| 3912 | 3899 |
| 3913 // Constants encoding the write-index for the state transition into the top 5 | 3900 // Constants encoding the write-index for the state transition into the top 5 |
| 3914 // bits of a byte. | 3901 // bits of a byte. |
| 3915 const int schemeEnd = _schemeEndIndex << 5; | 3902 const int schemeEnd = _schemeEndIndex << 5; |
| 3916 const int hostStart = _hostStartIndex << 5; | 3903 const int hostStart = _hostStartIndex << 5; |
| 3917 const int portStart = _portStartIndex << 5; | 3904 const int portStart = _portStartIndex << 5; |
| 3918 const int pathStart = _pathStartIndex << 5; | 3905 const int pathStart = _pathStartIndex << 5; |
| 3919 const int queryStart = _queryStartIndex << 5; | 3906 const int queryStart = _queryStartIndex << 5; |
| 3920 const int fragmentStart = _fragmentStartIndex << 5; | 3907 const int fragmentStart = _fragmentStartIndex << 5; |
| 3921 const int notSimple = _notSimpleIndex << 5; | 3908 const int notSimple = _notSimpleIndex << 5; |
| 3922 | 3909 |
| 3923 /// The `unreserved` characters of RFC 3986. | 3910 /// The `unreserved` characters of RFC 3986. |
| 3924 const unreserved = | 3911 const unreserved = |
| 3925 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~" ; | 3912 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~"; |
| 3913 |
| 3926 /// The `sub-delim` characters of RFC 3986. | 3914 /// The `sub-delim` characters of RFC 3986. |
| 3927 const subDelims = r"!$&'()*+,;="; | 3915 const subDelims = r"!$&'()*+,;="; |
| 3928 // The `pchar` characters of RFC 3986: characters that may occur in a path, | 3916 // The `pchar` characters of RFC 3986: characters that may occur in a path, |
| 3929 // excluding escapes. | 3917 // excluding escapes. |
| 3930 const pchar = "$unreserved$subDelims"; | 3918 const pchar = "$unreserved$subDelims"; |
| 3931 | 3919 |
| 3932 var tables = new List<Uint8List>.generate(stateCount, | 3920 var tables = |
| 3933 (_) => new Uint8List(96)); | 3921 new List<Uint8List>.generate(stateCount, (_) => new Uint8List(96)); |
| 3934 | 3922 |
| 3935 // Helper function which initialize the table for [state] with a default | 3923 // Helper function which initialize the table for [state] with a default |
| 3936 // transition and returns the table. | 3924 // transition and returns the table. |
| 3937 Uint8List build(state, defaultTransition) => | 3925 Uint8List build(state, defaultTransition) => |
| 3938 tables[state]..fillRange(0, 96, defaultTransition); | 3926 tables[state]..fillRange(0, 96, defaultTransition); |
| 3939 | 3927 |
| 3940 // Helper function which sets the transition for each character in [chars] | 3928 // Helper function which sets the transition for each character in [chars] |
| 3941 // to [transition] in the [target] table. | 3929 // to [transition] in the [target] table. |
| 3942 // The [chars] string must contain only characters in the U+0020 .. U+007E | 3930 // The [chars] string must contain only characters in the U+0020 .. U+007E |
| 3943 // range. | 3931 // range. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 3960 } | 3948 } |
| 3961 } | 3949 } |
| 3962 | 3950 |
| 3963 // Create the transitions for each state. | 3951 // Create the transitions for each state. |
| 3964 var b; | 3952 var b; |
| 3965 | 3953 |
| 3966 // Validate as path, if it is a scheme, we handle it later. | 3954 // Validate as path, if it is a scheme, we handle it later. |
| 3967 b = build(_uriStart, schemeOrPath | notSimple); | 3955 b = build(_uriStart, schemeOrPath | notSimple); |
| 3968 setChars(b, pchar, schemeOrPath); | 3956 setChars(b, pchar, schemeOrPath); |
| 3969 setChars(b, ".", schemeOrPathDot); | 3957 setChars(b, ".", schemeOrPathDot); |
| 3970 setChars(b, ":", authOrPath | schemeEnd); // Handle later. | 3958 setChars(b, ":", authOrPath | schemeEnd); // Handle later. |
| 3971 setChars(b, "/", authOrPathSlash); | 3959 setChars(b, "/", authOrPathSlash); |
| 3972 setChars(b, "?", query | queryStart); | 3960 setChars(b, "?", query | queryStart); |
| 3973 setChars(b, "#", fragment | fragmentStart); | 3961 setChars(b, "#", fragment | fragmentStart); |
| 3974 | 3962 |
| 3975 b = build(schemeOrPathDot, schemeOrPath | notSimple); | 3963 b = build(schemeOrPathDot, schemeOrPath | notSimple); |
| 3976 setChars(b, pchar, schemeOrPath); | 3964 setChars(b, pchar, schemeOrPath); |
| 3977 setChars(b, ".", schemeOrPathDot2); | 3965 setChars(b, ".", schemeOrPathDot2); |
| 3978 setChars(b, ':', authOrPath | schemeEnd); | 3966 setChars(b, ':', authOrPath | schemeEnd); |
| 3979 setChars(b, "/", pathSeg | notSimple); | 3967 setChars(b, "/", pathSeg | notSimple); |
| 3980 setChars(b, "?", query | queryStart); | 3968 setChars(b, "?", query | queryStart); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4055 b = build(relPathSegDot, path | notSimple); | 4043 b = build(relPathSegDot, path | notSimple); |
| 4056 setChars(b, pchar, path); | 4044 setChars(b, pchar, path); |
| 4057 setChars(b, ".", relPathSegDot2); | 4045 setChars(b, ".", relPathSegDot2); |
| 4058 setChars(b, "/", pathSeg | notSimple); | 4046 setChars(b, "/", pathSeg | notSimple); |
| 4059 setChars(b, "?", query | queryStart); | 4047 setChars(b, "?", query | queryStart); |
| 4060 setChars(b, "#", fragment | fragmentStart); | 4048 setChars(b, "#", fragment | fragmentStart); |
| 4061 | 4049 |
| 4062 b = build(relPathSegDot2, path | notSimple); | 4050 b = build(relPathSegDot2, path | notSimple); |
| 4063 setChars(b, pchar, path); | 4051 setChars(b, pchar, path); |
| 4064 setChars(b, "/", relPathSeg); | 4052 setChars(b, "/", relPathSeg); |
| 4065 setChars(b, "?", query | queryStart); // This should be non-simple. | 4053 setChars(b, "?", query | queryStart); // This should be non-simple. |
| 4066 setChars(b, "#", fragment | fragmentStart); // This should be non-simple. | 4054 setChars(b, "#", fragment | fragmentStart); // This should be non-simple. |
| 4067 | 4055 |
| 4068 b = build(pathSeg, path | notSimple); | 4056 b = build(pathSeg, path | notSimple); |
| 4069 setChars(b, pchar, path); | 4057 setChars(b, pchar, path); |
| 4070 setChars(b, ".", pathSegDot); | 4058 setChars(b, ".", pathSegDot); |
| 4071 setChars(b, "/", pathSeg | notSimple); | 4059 setChars(b, "/", pathSeg | notSimple); |
| 4072 setChars(b, "?", query | queryStart); | 4060 setChars(b, "?", query | queryStart); |
| 4073 setChars(b, "#", fragment | fragmentStart); | 4061 setChars(b, "#", fragment | fragmentStart); |
| 4074 | 4062 |
| 4075 b = build(pathSegDot, path | notSimple); | 4063 b = build(pathSegDot, path | notSimple); |
| 4076 setChars(b, pchar, path); | 4064 setChars(b, pchar, path); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4139 } | 4127 } |
| 4140 | 4128 |
| 4141 class _SimpleUri implements Uri { | 4129 class _SimpleUri implements Uri { |
| 4142 final String _uri; | 4130 final String _uri; |
| 4143 final int _schemeEnd; | 4131 final int _schemeEnd; |
| 4144 final int _hostStart; | 4132 final int _hostStart; |
| 4145 final int _portStart; | 4133 final int _portStart; |
| 4146 final int _pathStart; | 4134 final int _pathStart; |
| 4147 final int _queryStart; | 4135 final int _queryStart; |
| 4148 final int _fragmentStart; | 4136 final int _fragmentStart; |
| 4137 |
| 4149 /// The scheme is often used to distinguish URIs. | 4138 /// The scheme is often used to distinguish URIs. |
| 4150 /// To make comparisons more efficient, we cache the value, and | 4139 /// To make comparisons more efficient, we cache the value, and |
| 4151 /// canonicalize a few known types. | 4140 /// canonicalize a few known types. |
| 4152 String _schemeCache; | 4141 String _schemeCache; |
| 4153 int _hashCodeCache; | 4142 int _hashCodeCache; |
| 4154 | 4143 |
| 4155 _SimpleUri( | 4144 _SimpleUri( |
| 4156 this._uri, | 4145 this._uri, |
| 4157 this._schemeEnd, | 4146 this._schemeEnd, |
| 4158 this._hostStart, | 4147 this._hostStart, |
| 4159 this._portStart, | 4148 this._portStart, |
| 4160 this._pathStart, | 4149 this._pathStart, |
| 4161 this._queryStart, | 4150 this._queryStart, |
| 4162 this._fragmentStart, | 4151 this._fragmentStart, |
| 4163 this._schemeCache); | 4152 this._schemeCache); |
| 4164 | 4153 |
| 4165 bool get hasScheme => _schemeEnd > 0; | 4154 bool get hasScheme => _schemeEnd > 0; |
| 4166 bool get hasAuthority => _hostStart > 0; | 4155 bool get hasAuthority => _hostStart > 0; |
| 4167 bool get hasUserInfo => _hostStart > _schemeEnd + 4; | 4156 bool get hasUserInfo => _hostStart > _schemeEnd + 4; |
| 4168 bool get hasPort => _hostStart > 0 && _portStart + 1 < _pathStart; | 4157 bool get hasPort => _hostStart > 0 && _portStart + 1 < _pathStart; |
| 4169 bool get hasQuery => _queryStart < _fragmentStart; | 4158 bool get hasQuery => _queryStart < _fragmentStart; |
| 4170 bool get hasFragment => _fragmentStart < _uri.length; | 4159 bool get hasFragment => _fragmentStart < _uri.length; |
| 4171 | 4160 |
| 4172 bool get _isFile => _schemeEnd == 4 && _uri.startsWith("file"); | 4161 bool get _isFile => _schemeEnd == 4 && _uri.startsWith("file"); |
| 4173 bool get _isHttp => _schemeEnd == 4 && _uri.startsWith("http"); | 4162 bool get _isHttp => _schemeEnd == 4 && _uri.startsWith("http"); |
| 4174 bool get _isHttps => _schemeEnd == 5 && _uri.startsWith("https"); | 4163 bool get _isHttps => _schemeEnd == 5 && _uri.startsWith("https"); |
| 4175 bool get _isPackage => _schemeEnd == 7 && _uri.startsWith("package"); | 4164 bool get _isPackage => _schemeEnd == 7 && _uri.startsWith("package"); |
| 4165 |
| 4176 /// Like [isScheme] but expects argument to be case normalized. | 4166 /// Like [isScheme] but expects argument to be case normalized. |
| 4177 bool _isScheme(String scheme) => | 4167 bool _isScheme(String scheme) => |
| 4178 _schemeEnd == scheme.length && _uri.startsWith(scheme); | 4168 _schemeEnd == scheme.length && _uri.startsWith(scheme); |
| 4179 | 4169 |
| 4180 bool get hasAbsolutePath => _uri.startsWith("/", _pathStart); | 4170 bool get hasAbsolutePath => _uri.startsWith("/", _pathStart); |
| 4181 bool get hasEmptyPath => _pathStart == _queryStart; | 4171 bool get hasEmptyPath => _pathStart == _queryStart; |
| 4182 | 4172 |
| 4183 bool get isAbsolute => hasScheme && !hasFragment; | 4173 bool get isAbsolute => hasScheme && !hasFragment; |
| 4184 | 4174 |
| 4185 bool isScheme(String scheme) { | 4175 bool isScheme(String scheme) { |
| 4186 if (scheme == null || scheme.isEmpty) return _schemeEnd < 0; | 4176 if (scheme == null || scheme.isEmpty) return _schemeEnd < 0; |
| 4187 if (scheme.length != _schemeEnd) return false; | 4177 if (scheme.length != _schemeEnd) return false; |
| 4188 return _Uri._compareScheme(scheme, _uri); | 4178 return _Uri._compareScheme(scheme, _uri); |
| 4189 } | 4179 } |
| 4190 | 4180 |
| 4191 String get scheme { | 4181 String get scheme { |
| 4192 if (_schemeEnd <= 0) return ""; | 4182 if (_schemeEnd <= 0) return ""; |
| 4193 if (_schemeCache != null) return _schemeCache; | 4183 if (_schemeCache != null) return _schemeCache; |
| 4194 if (_isHttp) { | 4184 if (_isHttp) { |
| 4195 _schemeCache = "http"; | 4185 _schemeCache = "http"; |
| 4196 } else if (_isHttps) { | 4186 } else if (_isHttps) { |
| 4197 _schemeCache = "https"; | 4187 _schemeCache = "https"; |
| 4198 } else if (_isFile) { | 4188 } else if (_isFile) { |
| 4199 _schemeCache = "file"; | 4189 _schemeCache = "file"; |
| 4200 } else if (_isPackage) { | 4190 } else if (_isPackage) { |
| 4201 _schemeCache = "package"; | 4191 _schemeCache = "package"; |
| 4202 } else { | 4192 } else { |
| 4203 _schemeCache = _uri.substring(0, _schemeEnd); | 4193 _schemeCache = _uri.substring(0, _schemeEnd); |
| 4204 } | 4194 } |
| 4205 return _schemeCache; | 4195 return _schemeCache; |
| 4206 } | 4196 } |
| 4207 String get authority => _hostStart > 0 ? | 4197 |
| 4208 _uri.substring(_schemeEnd + 3, _pathStart) : ""; | 4198 String get authority => |
| 4209 String get userInfo => (_hostStart > _schemeEnd + 3) ? | 4199 _hostStart > 0 ? _uri.substring(_schemeEnd + 3, _pathStart) : ""; |
| 4210 _uri.substring(_schemeEnd + 3, _hostStart - 1) : ""; | 4200 String get userInfo => (_hostStart > _schemeEnd + 3) |
| 4201 ? _uri.substring(_schemeEnd + 3, _hostStart - 1) |
| 4202 : ""; |
| 4211 String get host => | 4203 String get host => |
| 4212 _hostStart > 0 ? _uri.substring(_hostStart, _portStart) : ""; | 4204 _hostStart > 0 ? _uri.substring(_hostStart, _portStart) : ""; |
| 4213 int get port { | 4205 int get port { |
| 4214 if (hasPort) return int.parse(_uri.substring(_portStart + 1, _pathStart)); | 4206 if (hasPort) return int.parse(_uri.substring(_portStart + 1, _pathStart)); |
| 4215 if (_isHttp) return 80; | 4207 if (_isHttp) return 80; |
| 4216 if (_isHttps) return 443; | 4208 if (_isHttps) return 443; |
| 4217 return 0; | 4209 return 0; |
| 4218 } | 4210 } |
| 4219 String get path =>_uri.substring(_pathStart, _queryStart); | 4211 |
| 4220 String get query => (_queryStart < _fragmentStart) ? | 4212 String get path => _uri.substring(_pathStart, _queryStart); |
| 4221 _uri.substring(_queryStart + 1, _fragmentStart) : ""; | 4213 String get query => (_queryStart < _fragmentStart) |
| 4222 String get fragment => (_fragmentStart < _uri.length) ? | 4214 ? _uri.substring(_queryStart + 1, _fragmentStart) |
| 4223 _uri.substring(_fragmentStart + 1) : ""; | 4215 : ""; |
| 4216 String get fragment => |
| 4217 (_fragmentStart < _uri.length) ? _uri.substring(_fragmentStart + 1) : ""; |
| 4224 | 4218 |
| 4225 String get origin { | 4219 String get origin { |
| 4226 // Check original behavior - W3C spec is wonky! | 4220 // Check original behavior - W3C spec is wonky! |
| 4227 bool isHttp = _isHttp; | 4221 bool isHttp = _isHttp; |
| 4228 if (_schemeEnd < 0) { | 4222 if (_schemeEnd < 0) { |
| 4229 throw new StateError("Cannot use origin without a scheme: $this"); | 4223 throw new StateError("Cannot use origin without a scheme: $this"); |
| 4230 } | 4224 } |
| 4231 if (!isHttp && !_isHttps) { | 4225 if (!isHttp && !_isHttps) { |
| 4232 throw new StateError( | 4226 throw new StateError( |
| 4233 "Origin is only applicable schemes http and https: $this"); | 4227 "Origin is only applicable to schemes http and https: $this"); |
| 4234 } | 4228 } |
| 4235 if (_hostStart == _portStart) { | 4229 if (_hostStart == _portStart) { |
| 4236 throw new StateError( | 4230 throw new StateError( |
| 4237 "A $scheme: URI should have a non-empty host name: $this"); | 4231 "A $scheme: URI should have a non-empty host name: $this"); |
| 4238 } | 4232 } |
| 4239 if (_hostStart == _schemeEnd + 3) { | 4233 if (_hostStart == _schemeEnd + 3) { |
| 4240 return _uri.substring(0, _pathStart); | 4234 return _uri.substring(0, _pathStart); |
| 4241 } | 4235 } |
| 4242 // Need to drop anon-empty userInfo. | 4236 // Need to drop anon-empty userInfo. |
| 4243 return _uri.substring(0, _schemeEnd + 3) + | 4237 return _uri.substring(0, _schemeEnd + 3) + |
| 4244 _uri.substring(_hostStart, _pathStart); | 4238 _uri.substring(_hostStart, _pathStart); |
| 4245 } | 4239 } |
| 4246 | 4240 |
| 4247 List<String> get pathSegments { | 4241 List<String> get pathSegments { |
| 4248 int start = _pathStart; | 4242 int start = _pathStart; |
| 4249 int end = _queryStart; | 4243 int end = _queryStart; |
| 4250 if (_uri.startsWith("/", start)) start++; | 4244 if (_uri.startsWith("/", start)) start++; |
| 4251 if (start == end) return const <String>[]; | 4245 if (start == end) return const <String>[]; |
| 4252 List<String> parts = []; | 4246 List<String> parts = []; |
| 4253 for (int i = start; i < end; i++) { | 4247 for (int i = start; i < end; i++) { |
| 4254 var char = _uri.codeUnitAt(i); | 4248 var char = _uri.codeUnitAt(i); |
| 4255 if (char == _SLASH) { | 4249 if (char == _SLASH) { |
| 4256 parts.add(_uri.substring(start, i)); | 4250 parts.add(_uri.substring(start, i)); |
| 4257 start = i + 1; | 4251 start = i + 1; |
| 4258 } | 4252 } |
| 4259 } | 4253 } |
| 4260 parts.add(_uri.substring(start, end)); | 4254 parts.add(_uri.substring(start, end)); |
| 4261 return new List<String>.unmodifiable(parts); | 4255 return new List<String>.unmodifiable(parts); |
| 4262 } | 4256 } |
| 4263 | 4257 |
| 4264 Map<String, String> get queryParameters { | 4258 Map<String, String> get queryParameters { |
| 4265 if (!hasQuery) return const <String, String>{}; | 4259 if (!hasQuery) return const <String, String>{}; |
| 4266 return new UnmodifiableMapView<String, String>( | 4260 return new UnmodifiableMapView<String, String>(Uri.splitQueryString(query)); |
| 4267 Uri.splitQueryString(query)); | |
| 4268 } | 4261 } |
| 4269 | 4262 |
| 4270 Map<String, List<String>> get queryParametersAll { | 4263 Map<String, List<String>> get queryParametersAll { |
| 4271 if (!hasQuery) return const <String, List<String>>{}; | 4264 if (!hasQuery) return const <String, List<String>>{}; |
| 4272 Map queryParameterLists = _Uri._splitQueryStringAll(query); | 4265 Map queryParameterLists = _Uri._splitQueryStringAll(query); |
| 4273 for (var key in queryParameterLists.keys) { | 4266 for (var key in queryParameterLists.keys) { |
| 4274 queryParameterLists[key] = | 4267 queryParameterLists[key] = |
| 4275 new List<String>.unmodifiable(queryParameterLists[key]); | 4268 new List<String>.unmodifiable(queryParameterLists[key]); |
| 4276 } | 4269 } |
| 4277 return new Map<String, List<String>>.unmodifiable(queryParameterLists); | 4270 return new Map<String, List<String>>.unmodifiable(queryParameterLists); |
| 4278 } | 4271 } |
| 4279 | 4272 |
| 4280 bool _isPort(String port) { | 4273 bool _isPort(String port) { |
| 4281 int portDigitStart = _portStart + 1; | 4274 int portDigitStart = _portStart + 1; |
| 4282 return portDigitStart + port.length == _pathStart && | 4275 return portDigitStart + port.length == _pathStart && |
| 4283 _uri.startsWith(port, portDigitStart); | 4276 _uri.startsWith(port, portDigitStart); |
| 4284 } | 4277 } |
| 4285 | 4278 |
| 4286 Uri normalizePath() => this; | 4279 Uri normalizePath() => this; |
| 4287 | 4280 |
| 4288 Uri removeFragment() { | 4281 Uri removeFragment() { |
| 4289 if (!hasFragment) return this; | 4282 if (!hasFragment) return this; |
| 4290 return new _SimpleUri( | 4283 return new _SimpleUri( |
| 4291 _uri.substring(0, _fragmentStart), | 4284 _uri.substring(0, _fragmentStart), |
| 4292 _schemeEnd, _hostStart, _portStart, | 4285 _schemeEnd, |
| 4293 _pathStart, _queryStart, _fragmentStart, _schemeCache); | 4286 _hostStart, |
| 4287 _portStart, |
| 4288 _pathStart, |
| 4289 _queryStart, |
| 4290 _fragmentStart, |
| 4291 _schemeCache); |
| 4294 } | 4292 } |
| 4295 | 4293 |
| 4296 Uri replace({String scheme, | 4294 Uri replace( |
| 4297 String userInfo, | 4295 {String scheme, |
| 4298 String host, | 4296 String userInfo, |
| 4299 int port, | 4297 String host, |
| 4300 String path, | 4298 int port, |
| 4301 Iterable<String> pathSegments, | 4299 String path, |
| 4302 String query, | 4300 Iterable<String> pathSegments, |
| 4303 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, | 4301 String query, |
| 4304 String fragment}) { | 4302 Map<String, dynamic /*String|Iterable<String>*/ > queryParameters, |
| 4303 String fragment}) { |
| 4305 bool schemeChanged = false; | 4304 bool schemeChanged = false; |
| 4306 if (scheme != null) { | 4305 if (scheme != null) { |
| 4307 scheme = _Uri._makeScheme(scheme, 0, scheme.length); | 4306 scheme = _Uri._makeScheme(scheme, 0, scheme.length); |
| 4308 schemeChanged = !_isScheme(scheme); | 4307 schemeChanged = !_isScheme(scheme); |
| 4309 } else { | 4308 } else { |
| 4310 scheme = this.scheme; | 4309 scheme = this.scheme; |
| 4311 } | 4310 } |
| 4312 bool isFile = (scheme == "file"); | 4311 bool isFile = (scheme == "file"); |
| 4313 if (userInfo != null) { | 4312 if (userInfo != null) { |
| 4314 userInfo = _Uri._makeUserInfo(userInfo, 0, userInfo.length); | 4313 userInfo = _Uri._makeUserInfo(userInfo, 0, userInfo.length); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 4330 host = _Uri._makeHost(host, 0, host.length, false); | 4329 host = _Uri._makeHost(host, 0, host.length, false); |
| 4331 } else if (_hostStart > 0) { | 4330 } else if (_hostStart > 0) { |
| 4332 host = _uri.substring(_hostStart, _portStart); | 4331 host = _uri.substring(_hostStart, _portStart); |
| 4333 } else if (userInfo.isNotEmpty || port != null || isFile) { | 4332 } else if (userInfo.isNotEmpty || port != null || isFile) { |
| 4334 host = ""; | 4333 host = ""; |
| 4335 } | 4334 } |
| 4336 | 4335 |
| 4337 bool hasAuthority = host != null; | 4336 bool hasAuthority = host != null; |
| 4338 if (path != null || pathSegments != null) { | 4337 if (path != null || pathSegments != null) { |
| 4339 path = _Uri._makePath(path, 0, _stringOrNullLength(path), pathSegments, | 4338 path = _Uri._makePath(path, 0, _stringOrNullLength(path), pathSegments, |
| 4340 scheme, hasAuthority); | 4339 scheme, hasAuthority); |
| 4341 } else { | 4340 } else { |
| 4342 path = _uri.substring(_pathStart, _queryStart); | 4341 path = _uri.substring(_pathStart, _queryStart); |
| 4343 if ((isFile || (hasAuthority && !path.isEmpty)) && | 4342 if ((isFile || (hasAuthority && !path.isEmpty)) && |
| 4344 !path.startsWith('/')) { | 4343 !path.startsWith('/')) { |
| 4345 path = "/" + path; | 4344 path = "/" + path; |
| 4346 } | 4345 } |
| 4347 } | 4346 } |
| 4348 | 4347 |
| 4349 if (query != null || queryParameters != null) { | 4348 if (query != null || queryParameters != null) { |
| 4350 query = _Uri._makeQuery( | 4349 query = _Uri._makeQuery( |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4387 if (base._isFile) { | 4386 if (base._isFile) { |
| 4388 isSimple = !ref.hasEmptyPath; | 4387 isSimple = !ref.hasEmptyPath; |
| 4389 } else if (base._isHttp) { | 4388 } else if (base._isHttp) { |
| 4390 isSimple = !ref._isPort("80"); | 4389 isSimple = !ref._isPort("80"); |
| 4391 } else if (base._isHttps) { | 4390 } else if (base._isHttps) { |
| 4392 isSimple = !ref._isPort("443"); | 4391 isSimple = !ref._isPort("443"); |
| 4393 } | 4392 } |
| 4394 if (isSimple) { | 4393 if (isSimple) { |
| 4395 var delta = base._schemeEnd + 1; | 4394 var delta = base._schemeEnd + 1; |
| 4396 var newUri = base._uri.substring(0, base._schemeEnd + 1) + | 4395 var newUri = base._uri.substring(0, base._schemeEnd + 1) + |
| 4397 ref._uri.substring(ref._schemeEnd + 1); | 4396 ref._uri.substring(ref._schemeEnd + 1); |
| 4398 return new _SimpleUri(newUri, | 4397 return new _SimpleUri( |
| 4399 base._schemeEnd, | 4398 newUri, |
| 4400 ref._hostStart + delta, | 4399 base._schemeEnd, |
| 4401 ref._portStart + delta, | 4400 ref._hostStart + delta, |
| 4402 ref._pathStart + delta, | 4401 ref._portStart + delta, |
| 4403 ref._queryStart + delta, | 4402 ref._pathStart + delta, |
| 4404 ref._fragmentStart + delta, | 4403 ref._queryStart + delta, |
| 4405 base._schemeCache); | 4404 ref._fragmentStart + delta, |
| 4405 base._schemeCache); |
| 4406 } else { | 4406 } else { |
| 4407 // This will require normalization, so use the _Uri implementation. | 4407 // This will require normalization, so use the _Uri implementation. |
| 4408 return _toNonSimple().resolveUri(ref); | 4408 return _toNonSimple().resolveUri(ref); |
| 4409 } | 4409 } |
| 4410 } | 4410 } |
| 4411 if (ref.hasEmptyPath) { | 4411 if (ref.hasEmptyPath) { |
| 4412 if (ref.hasQuery) { | 4412 if (ref.hasQuery) { |
| 4413 int delta = base._queryStart - ref._queryStart; | 4413 int delta = base._queryStart - ref._queryStart; |
| 4414 var newUri = base._uri.substring(0, base._queryStart) + | 4414 var newUri = base._uri.substring(0, base._queryStart) + |
| 4415 ref._uri.substring(ref._queryStart); | 4415 ref._uri.substring(ref._queryStart); |
| 4416 return new _SimpleUri(newUri, | 4416 return new _SimpleUri( |
| 4417 base._schemeEnd, | 4417 newUri, |
| 4418 base._hostStart, | 4418 base._schemeEnd, |
| 4419 base._portStart, | 4419 base._hostStart, |
| 4420 base._pathStart, | 4420 base._portStart, |
| 4421 ref._queryStart + delta, | 4421 base._pathStart, |
| 4422 ref._fragmentStart + delta, | 4422 ref._queryStart + delta, |
| 4423 base._schemeCache); | 4423 ref._fragmentStart + delta, |
| 4424 base._schemeCache); |
| 4424 } | 4425 } |
| 4425 if (ref.hasFragment) { | 4426 if (ref.hasFragment) { |
| 4426 int delta = base._fragmentStart - ref._fragmentStart; | 4427 int delta = base._fragmentStart - ref._fragmentStart; |
| 4427 var newUri = base._uri.substring(0, base._fragmentStart) + | 4428 var newUri = base._uri.substring(0, base._fragmentStart) + |
| 4428 ref._uri.substring(ref._fragmentStart); | 4429 ref._uri.substring(ref._fragmentStart); |
| 4429 return new _SimpleUri(newUri, | 4430 return new _SimpleUri( |
| 4430 base._schemeEnd, | 4431 newUri, |
| 4431 base._hostStart, | 4432 base._schemeEnd, |
| 4432 base._portStart, | 4433 base._hostStart, |
| 4433 base._pathStart, | 4434 base._portStart, |
| 4434 base._queryStart, | 4435 base._pathStart, |
| 4435 ref._fragmentStart + delta, | 4436 base._queryStart, |
| 4436 base._schemeCache); | 4437 ref._fragmentStart + delta, |
| 4438 base._schemeCache); |
| 4437 } | 4439 } |
| 4438 return base.removeFragment(); | 4440 return base.removeFragment(); |
| 4439 } | 4441 } |
| 4440 if (ref.hasAbsolutePath) { | 4442 if (ref.hasAbsolutePath) { |
| 4441 var delta = base._pathStart - ref._pathStart; | 4443 var delta = base._pathStart - ref._pathStart; |
| 4442 var newUri = base._uri.substring(0, base._pathStart) + | 4444 var newUri = base._uri.substring(0, base._pathStart) + |
| 4443 ref._uri.substring(ref._pathStart); | 4445 ref._uri.substring(ref._pathStart); |
| 4444 return new _SimpleUri(newUri, | 4446 return new _SimpleUri( |
| 4445 base._schemeEnd, | 4447 newUri, |
| 4446 base._hostStart, | 4448 base._schemeEnd, |
| 4447 base._portStart, | 4449 base._hostStart, |
| 4448 base._pathStart, | 4450 base._portStart, |
| 4449 ref._queryStart + delta, | 4451 base._pathStart, |
| 4450 ref._fragmentStart + delta, | 4452 ref._queryStart + delta, |
| 4451 base._schemeCache); | 4453 ref._fragmentStart + delta, |
| 4454 base._schemeCache); |
| 4452 } | 4455 } |
| 4453 if (base.hasEmptyPath && base.hasAuthority) { | 4456 if (base.hasEmptyPath && base.hasAuthority) { |
| 4454 // ref has relative non-empty path. | 4457 // ref has relative non-empty path. |
| 4455 // Add a "/" in front, then leading "/../" segments are folded to "/". | 4458 // Add a "/" in front, then leading "/../" segments are folded to "/". |
| 4456 int refStart = ref._pathStart; | 4459 int refStart = ref._pathStart; |
| 4457 while (ref._uri.startsWith("../", refStart)) { | 4460 while (ref._uri.startsWith("../", refStart)) { |
| 4458 refStart += 3; | 4461 refStart += 3; |
| 4459 } | 4462 } |
| 4460 var delta = base._pathStart - refStart + 1; | 4463 var delta = base._pathStart - refStart + 1; |
| 4461 var newUri = "${base._uri.substring(0, base._pathStart)}/" | 4464 var newUri = "${base._uri.substring(0, base._pathStart)}/" |
| 4462 "${ref._uri.substring(refStart)}"; | 4465 "${ref._uri.substring(refStart)}"; |
| 4463 return new _SimpleUri(newUri, | 4466 return new _SimpleUri( |
| 4464 base._schemeEnd, | 4467 newUri, |
| 4465 base._hostStart, | 4468 base._schemeEnd, |
| 4466 base._portStart, | 4469 base._hostStart, |
| 4467 base._pathStart, | 4470 base._portStart, |
| 4468 ref._queryStart + delta, | 4471 base._pathStart, |
| 4469 ref._fragmentStart + delta, | 4472 ref._queryStart + delta, |
| 4470 base._schemeCache); | 4473 ref._fragmentStart + delta, |
| 4474 base._schemeCache); |
| 4471 } | 4475 } |
| 4472 // Merge paths. | 4476 // Merge paths. |
| 4473 | 4477 |
| 4474 // The RFC 3986 algorithm merges the base path without its final segment | 4478 // The RFC 3986 algorithm merges the base path without its final segment |
| 4475 // (anything after the final "/", or everything if the base path doesn't | 4479 // (anything after the final "/", or everything if the base path doesn't |
| 4476 // contain any "/"), and the reference path. | 4480 // contain any "/"), and the reference path. |
| 4477 // Then it removes "." and ".." segments using the remove-dot-segment | 4481 // Then it removes "." and ".." segments using the remove-dot-segment |
| 4478 // algorithm. | 4482 // algorithm. |
| 4479 // This code combines the two steps. It is simplified by knowing that | 4483 // This code combines the two steps. It is simplified by knowing that |
| 4480 // the base path contains no "." or ".." segments, and the reference | 4484 // the base path contains no "." or ".." segments, and the reference |
| 4481 // path can only contain leading ".." segments. | 4485 // path can only contain leading ".." segments. |
| 4482 | 4486 |
| 4483 String baseUri = base._uri; | 4487 String baseUri = base._uri; |
| 4484 String refUri = ref._uri; | 4488 String refUri = ref._uri; |
| 4485 int baseStart = base._pathStart; | 4489 int baseStart = base._pathStart; |
| 4486 int baseEnd = base._queryStart; | 4490 int baseEnd = base._queryStart; |
| 4487 while (baseUri.startsWith("../", baseStart)) baseStart += 3; | 4491 while (baseUri.startsWith("../", baseStart)) baseStart += 3; |
| 4488 int refStart = ref._pathStart; | 4492 int refStart = ref._pathStart; |
| 4489 int refEnd = ref._queryStart; | 4493 int refEnd = ref._queryStart; |
| 4490 | 4494 |
| 4491 /// Count of leading ".." segments in reference path. | 4495 /// Count of leading ".." segments in reference path. |
| 4492 /// The count is decremented when the segment is matched with a | 4496 /// The count is decremented when the segment is matched with a |
| 4493 /// segment of the base path, and both are then omitted from the result. | 4497 /// segment of the base path, and both are then omitted from the result. |
| 4494 int backCount = 0; | 4498 int backCount = 0; |
| 4499 |
| 4495 /// Count "../" segments and advance `refStart` to after the segments. | 4500 /// Count "../" segments and advance `refStart` to after the segments. |
| 4496 while (refStart + 3 <= refEnd && refUri.startsWith("../", refStart)) { | 4501 while (refStart + 3 <= refEnd && refUri.startsWith("../", refStart)) { |
| 4497 refStart += 3; | 4502 refStart += 3; |
| 4498 backCount += 1; | 4503 backCount += 1; |
| 4499 } | 4504 } |
| 4500 | 4505 |
| 4501 // Extra slash inserted between base and reference path parts if | 4506 // Extra slash inserted between base and reference path parts if |
| 4502 // the base path contains any slashes, or empty string if none. | 4507 // the base path contains any slashes, or empty string if none. |
| 4503 // (We could use a slash from the base path in most cases, but not if | 4508 // (We could use a slash from the base path in most cases, but not if |
| 4504 // we remove the entire base path). | 4509 // we remove the entire base path). |
| (...skipping 23 matching lines...) Expand all Loading... |
| 4528 // We reached the start of the base path, and want to stay relative, | 4533 // We reached the start of the base path, and want to stay relative, |
| 4529 // so don't insert a slash. | 4534 // so don't insert a slash. |
| 4530 insert = ""; | 4535 insert = ""; |
| 4531 // If we reached the start of the base path with more "../" left over | 4536 // If we reached the start of the base path with more "../" left over |
| 4532 // in the reference path, include those segments in the result. | 4537 // in the reference path, include those segments in the result. |
| 4533 refStart -= backCount * 3; | 4538 refStart -= backCount * 3; |
| 4534 } | 4539 } |
| 4535 | 4540 |
| 4536 var delta = baseEnd - refStart + insert.length; | 4541 var delta = baseEnd - refStart + insert.length; |
| 4537 var newUri = "${base._uri.substring(0, baseEnd)}$insert" | 4542 var newUri = "${base._uri.substring(0, baseEnd)}$insert" |
| 4538 "${ref._uri.substring(refStart)}"; | 4543 "${ref._uri.substring(refStart)}"; |
| 4539 | 4544 |
| 4540 return new _SimpleUri(newUri, | 4545 return new _SimpleUri( |
| 4541 base._schemeEnd, | 4546 newUri, |
| 4542 base._hostStart, | 4547 base._schemeEnd, |
| 4543 base._portStart, | 4548 base._hostStart, |
| 4544 base._pathStart, | 4549 base._portStart, |
| 4545 ref._queryStart + delta, | 4550 base._pathStart, |
| 4546 ref._fragmentStart + delta, | 4551 ref._queryStart + delta, |
| 4547 base._schemeCache); | 4552 ref._fragmentStart + delta, |
| 4553 base._schemeCache); |
| 4548 } | 4554 } |
| 4549 | 4555 |
| 4550 String toFilePath({bool windows}) { | 4556 String toFilePath({bool windows}) { |
| 4551 if (_schemeEnd >= 0 && !_isFile) { | 4557 if (_schemeEnd >= 0 && !_isFile) { |
| 4552 throw new UnsupportedError( | 4558 throw new UnsupportedError( |
| 4553 "Cannot extract a file path from a $scheme URI"); | 4559 "Cannot extract a file path from a $scheme URI"); |
| 4554 } | 4560 } |
| 4555 if (_queryStart < _uri.length) { | 4561 if (_queryStart < _uri.length) { |
| 4556 if (_queryStart < _fragmentStart) { | 4562 if (_queryStart < _fragmentStart) { |
| 4557 throw new UnsupportedError( | 4563 throw new UnsupportedError( |
| 4558 "Cannot extract a file path from a URI with a query component"); | 4564 "Cannot extract a file path from a URI with a query component"); |
| 4559 } | 4565 } |
| 4560 throw new UnsupportedError( | 4566 throw new UnsupportedError( |
| 4561 "Cannot extract a file path from a URI with a fragment component"); | 4567 "Cannot extract a file path from a URI with a fragment component"); |
| 4562 } | 4568 } |
| 4563 if (windows == null) windows = _Uri._isWindows; | 4569 if (windows == null) windows = _Uri._isWindows; |
| 4564 return windows ? _Uri._toWindowsFilePath(this) : _toFilePath(); | 4570 return windows ? _Uri._toWindowsFilePath(this) : _toFilePath(); |
| 4565 } | 4571 } |
| 4566 | 4572 |
| 4567 String _toFilePath() { | 4573 String _toFilePath() { |
| 4568 if (_hostStart < _portStart) { | 4574 if (_hostStart < _portStart) { |
| 4569 // Has authority and non-empty host. | 4575 // Has authority and non-empty host. |
| 4570 throw new UnsupportedError( | 4576 throw new UnsupportedError( |
| 4571 "Cannot extract a non-Windows file path from a file URI " | 4577 "Cannot extract a non-Windows file path from a file URI " |
| 4572 "with an authority"); | 4578 "with an authority"); |
| 4573 } | 4579 } |
| 4574 return this.path; | 4580 return this.path; |
| 4575 } | 4581 } |
| 4576 | 4582 |
| 4577 UriData get data { | 4583 UriData get data { |
| 4578 assert(scheme != "data"); | 4584 assert(scheme != "data"); |
| 4579 return null; | 4585 return null; |
| 4580 } | 4586 } |
| 4581 | 4587 |
| 4582 int get hashCode => _hashCodeCache ??= _uri.hashCode; | 4588 int get hashCode => _hashCodeCache ??= _uri.hashCode; |
| 4583 | 4589 |
| 4584 bool operator==(Object other) { | 4590 bool operator ==(Object other) { |
| 4585 if (identical(this, other)) return true; | 4591 if (identical(this, other)) return true; |
| 4586 if (other is Uri) return _uri == other.toString(); | 4592 if (other is Uri) return _uri == other.toString(); |
| 4587 return false; | 4593 return false; |
| 4588 } | 4594 } |
| 4589 | 4595 |
| 4590 Uri _toNonSimple() { | 4596 Uri _toNonSimple() { |
| 4591 return new _Uri._internal( | 4597 return new _Uri._internal( |
| 4592 this.scheme, | 4598 this.scheme, |
| 4593 this.userInfo, | 4599 this.userInfo, |
| 4594 this.hasAuthority ? this.host: null, | 4600 this.hasAuthority ? this.host : null, |
| 4595 this.hasPort ? this.port : null, | 4601 this.hasPort ? this.port : null, |
| 4596 this.path, | 4602 this.path, |
| 4597 this.hasQuery ? this.query : null, | 4603 this.hasQuery ? this.query : null, |
| 4598 this.hasFragment ? this.fragment : null | 4604 this.hasFragment ? this.fragment : null); |
| 4599 ); | |
| 4600 } | 4605 } |
| 4601 | 4606 |
| 4602 String toString() => _uri; | 4607 String toString() => _uri; |
| 4603 } | 4608 } |
| 4604 | 4609 |
| 4605 /// Special [_Uri] created from an existing [UriData]. | 4610 /// Special [_Uri] created from an existing [UriData]. |
| 4606 class _DataUri extends _Uri { | 4611 class _DataUri extends _Uri { |
| 4607 final UriData _data; | 4612 final UriData _data; |
| 4608 | 4613 |
| 4609 _DataUri(this._data, String path, String query) | 4614 _DataUri(this._data, String path, String query) |
| 4610 : super._internal("data", null, null, null, path, query, null); | 4615 : super._internal("data", null, null, null, path, query, null); |
| 4611 | 4616 |
| 4612 UriData get data => _data; | 4617 UriData get data => _data; |
| 4613 } | 4618 } |
| 4614 | 4619 |
| 4615 /// Checks whether [text] starts with "data:" at position [start]. | 4620 /// Checks whether [text] starts with "data:" at position [start]. |
| 4616 /// | 4621 /// |
| 4617 /// The text must be long enough to allow reading five characters | 4622 /// The text must be long enough to allow reading five characters |
| 4618 /// from the [start] position. | 4623 /// from the [start] position. |
| 4619 /// | 4624 /// |
| 4620 /// Returns an integer value which is zero if text starts with all-lowercase | 4625 /// Returns an integer value which is zero if text starts with all-lowercase |
| 4621 /// "data:" and 0x20 if the text starts with "data:" that isn't all lower-case. | 4626 /// "data:" and 0x20 if the text starts with "data:" that isn't all lower-case. |
| 4622 /// All other values means the text starts with some other character. | 4627 /// All other values means the text starts with some other character. |
| 4623 int _startsWithData(String text, int start) { | 4628 int _startsWithData(String text, int start) { |
| 4624 // Multiply by 3 to avoid a non-colon character making delta be 0x20. | 4629 // Multiply by 3 to avoid a non-colon character making delta be 0x20. |
| 4625 int delta = (text.codeUnitAt(start + 4) ^ _COLON) * 3; | 4630 int delta = (text.codeUnitAt(start + 4) ^ _COLON) * 3; |
| 4626 delta |= text.codeUnitAt(start) ^ 0x64 /*d*/; | 4631 delta |= text.codeUnitAt(start) ^ 0x64 /*d*/; |
| 4627 delta |= text.codeUnitAt(start + 1) ^ 0x61 /*a*/; | 4632 delta |= text.codeUnitAt(start + 1) ^ 0x61 /*a*/; |
| 4628 delta |= text.codeUnitAt(start + 2) ^ 0x74 /*t*/; | 4633 delta |= text.codeUnitAt(start + 2) ^ 0x74 /*t*/; |
| 4629 delta |= text.codeUnitAt(start + 3) ^ 0x61 /*a*/; | 4634 delta |= text.codeUnitAt(start + 3) ^ 0x61 /*a*/; |
| 4630 return delta; | 4635 return delta; |
| 4631 } | 4636 } |
| 4632 | 4637 |
| 4633 /// Helper function returning the length of a string, or `0` for `null`. | 4638 /// Helper function returning the length of a string, or `0` for `null`. |
| 4634 int _stringOrNullLength(String s) => (s == null) ? 0 : s.length; | 4639 int _stringOrNullLength(String s) => (s == null) ? 0 : s.length; |
| OLD | NEW |