Chromium Code Reviews| Index: sdk/lib/core/uri.dart |
| diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart |
| index e4a31aa575fd0f40d23012d7b8f1382cf17d279c..917ade7a29dca48e311c303f17ac2d4862bf4660 100644 |
| --- a/sdk/lib/core/uri.dart |
| +++ b/sdk/lib/core/uri.dart |
| @@ -76,6 +76,7 @@ class Uri { |
| * Cache the computed return value of [queryParameters]. |
| */ |
| Map<String, String> _queryParameters; |
| + Map<String, List<String>> _queryParameterLists; |
| /// Internal non-verifying constructor. Only call with validated arguments. |
| Uri._internal(this.scheme, |
| @@ -136,14 +137,16 @@ class Uri { |
| * will be escaped if necessary. |
| * When [queryParameters] is used the query is built from the |
| * provided map. Each key and value in the map is percent-encoded |
| - * and joined using equal and ampersand characters. The |
| - * percent-encoding of the keys and values encodes all characters |
| - * except for the unreserved characters. |
| + * and joined using equal and ampersand characters. |
| + * A value in the map must be either a string, or a an [Iterable] of strings, |
| + * where the latter corresponds to multiple values for the same key. |
| + * The percent-encoding of the keys and values encodes all characters |
| + * except for the unreserved characters, and replaces spaces with `+`. |
|
floitsch
2016/01/12 16:51:33
Consider breaking it into paragraphs.
Use http://
Lasse Reichstein Nielsen
2016/01/13 12:30:14
More paragraphs added.
|
| * If `query` is the empty string, it is equivalent to omitting it. |
| * To have an actual empty query part, |
| * use an empty list for `queryParameters`. |
| - * If both `query` and `queryParameters` are omitted or `null`, the |
| - * URI will have no query part. |
| + * If both `query` and `queryParameters` are omitted or `null`, |
| + * the URI will have no query part. |
|
floitsch
2016/01/12 16:51:33
has no
Lasse Reichstein Nielsen
2016/01/13 12:30:14
Changed here, and in other places in the file.
|
| * |
| * The fragment component is set through [fragment]. |
| * It should be a valid URI fragment, but invalid characters other than |
| @@ -157,7 +160,7 @@ class Uri { |
| String path, |
| Iterable<String> pathSegments, |
| String query, |
| - Map<String, String> queryParameters, |
| + Map<String, dynamic> queryParameters, |
| String fragment}) { |
| scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); |
| userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); |
| @@ -1104,23 +1107,52 @@ class Uri { |
| * Returns the URI query split into a map according to the rules |
| * specified for FORM post in the [HTML 4.01 specification section |
| * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4"). |
| - * Each key and value in the returned map has been decoded. If there is no |
| - * query the empty map is returned. |
| + * Each key and value in the returned map has been decoded. |
| + * If there is no query the empty map is returned. |
| * |
| * Keys in the query string that have no value are mapped to the |
| * empty string. |
| + * If a key occurs more than once in the query string, it is mapped to |
| + * one of the values. The [queryParametersAll] getter can provide a |
|
floitsch
2016/01/12 16:51:33
which one?
Either explicitly state a "random" one,
Lasse Reichstein Nielsen
2016/01/13 12:30:14
Now say "arbitrary".
|
| + * map that maps to all the values. |
| * |
| - * The returned map is unmodifiable and will throw [UnsupportedError] on any |
| - * calls that would mutate it. |
| + * The returned map is unmodifiable. |
| */ |
| Map<String, String> get queryParameters { |
| if (_queryParameters == null) { |
| - _queryParameters = new UnmodifiableMapView(splitQueryString(query)); |
| + _queryParameters = |
| + new UnmodifiableMapView<String, String>(splitQueryString(query)); |
| } |
| return _queryParameters; |
| } |
| /** |
| + * Returns the URI query split into a map according to the rules |
| + * specified for FORM post in the [HTML 4.01 specification section |
| + * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTML 4.01 section 17.13.4"). |
|
floitsch
2016/01/12 16:51:33
make sure that this encodes correctly with dartdoc
Lasse Reichstein Nielsen
2016/01/13 12:30:14
Checks out correctly in the commonmark checker lin
|
| + * Each key and value in the returned map has been decoded. If there is no |
| + * query the empty map is returned. |
| + * |
| + * Keys are mapped to lists of their values. If a key occurs only once, |
| + * its value is a singleton list. If a key occurs with no value, the |
| + * empty string is used as the value for that occurrence. |
| + * |
| + * The returned map and the lists it contains are unmodifiable. |
| + */ |
| + Map<String, List<String>> get queryParametersAll { |
| + if (_queryParameterLists == null) { |
| + Map queryParameterLists = _splitQueryStringAll(query); |
| + for (var key in queryParameterLists.keys) { |
| + queryParameterLists[key] = |
| + new List<String>.unmodifiable(queryParameterLists[key]); |
| + } |
| + _queryParameterLists = |
| + new Map<String, List<String>>.unmodifiable(queryParameterLists); |
| + } |
| + return _queryParameterLists; |
| + } |
| + |
| + /** |
| * Returns a URI where the path has been normalized. |
| * |
| * A normalized path does not contain `.` segments or non-leading `..` |
| @@ -1344,17 +1376,27 @@ class Uri { |
| if (query != null) return _normalize(query, start, end, _queryCharTable); |
| var result = new StringBuffer(); |
| - var first = true; |
| - queryParameters.forEach((key, value) { |
| - if (!first) { |
| - result.write("&"); |
| - } |
| - first = false; |
| + var separator = ""; |
| + |
| + void writeParameter(String key, String value) { |
| + result.write(separator); |
| + separator = "&"; |
| result.write(Uri.encodeQueryComponent(key)); |
| - if (value != null && !value.isEmpty) { |
| + if (value != null && value.isNotEmpty) { |
| result.write("="); |
| result.write(Uri.encodeQueryComponent(value)); |
| } |
| + } |
| + |
| + queryParameters.forEach((key, value) { |
| + if (value == null || value is String) { |
| + writeParameter(key, value); |
| + } else { |
| + Iterable values = value; |
| + for (String value in values) { |
| + writeParameter(key, value); |
| + } |
| + } |
| }); |
| return result.toString(); |
| } |
| @@ -2156,6 +2198,46 @@ class Uri { |
| }); |
| } |
| + static List _createList() => []; |
| + |
| + static Map _splitQueryStringAll( |
| + String query, {Encoding encoding: UTF8}) { |
| + Map result = {}; |
| + int i = 0; |
| + int start = 0; |
| + int equalsIndex = -1; |
| + |
| + void parsePair(int start, int equalsIndex, int end) { |
| + String key; |
| + String value; |
| + if (start == end) return; |
| + if (equalsIndex < 0) { |
| + key = _uriDecode(query, start, end, encoding, true); |
| + value = ""; |
| + } else { |
| + key = _uriDecode(query, start, equalsIndex, encoding, true); |
| + value = _uriDecode(query, equalsIndex + 1, end, encoding, true); |
| + } |
| + result.putIfAbsent(key, _createList).add(value); |
| + } |
| + |
| + const int _equals = 0x3d; |
| + const int _ampersand = 0x26; |
| + while (i < query.length) { |
| + int char = query.codeUnitAt(i); |
| + if (char == _equals) { |
| + if (equalsIndex < 0) equalsIndex = i; |
| + } else if (char == _ampersand) { |
| + parsePair(start, equalsIndex, i); |
| + start = i + 1; |
| + equalsIndex = -1; |
| + } |
| + i++; |
| + } |
| + parsePair(start, equalsIndex, i); |
| + return result; |
| + } |
| + |
| /** |
| * Parse the [host] as an IP version 4 (IPv4) address, returning the address |
| * as a list of 4 bytes in network byte order (big endian). |