Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: sdk/lib/core/uri.dart

Issue 2754923002: Tweak corelib files so dartfmt can do a better job. Use // comments to force line wrapping for long… (Closed)
Patch Set: Tweak corelib files so dartfmt can do a better job. Use // comments to force line wrapping for long… Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sdk/lib/convert/base64.dart ('k') | sdk/lib/internal/internal.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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;
OLDNEW
« no previous file with comments | « sdk/lib/convert/base64.dart ('k') | sdk/lib/internal/internal.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698