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