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..660c8c87a592a248494cfa93cde8c3244b6b5146 100644 |
| --- a/sdk/lib/core/uri.dart |
| +++ b/sdk/lib/core/uri.dart |
| @@ -136,14 +136,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 `+`. |
| * 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. |
| * |
| * The fragment component is set through [fragment]. |
| * It should be a valid URI fragment, but invalid characters other than |
| @@ -157,7 +159,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 +1106,54 @@ 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 [queryParameterValues] getter can provide a |
|
floitsch
2015/12/11 19:00:58
which one?
Lasse Reichstein Nielsen
2016/01/12 13:34:11
I really don't want to promise that.
It's likely g
|
| + * map that maps to all the values. |
| * |
| * The returned map is unmodifiable and will throw [UnsupportedError] on any |
| * calls that would mutate it. |
| */ |
| 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"). |
| + * 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 |
| + * and will throw [UnsupportedError] on any calls that would mutate them. |
| + */ |
| + Map<String, List<String>> get queryParameterLists { |
| + 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 `..` |
| @@ -1350,10 +1383,23 @@ class Uri { |
| result.write("&"); |
| } |
| first = false; |
| - result.write(Uri.encodeQueryComponent(key)); |
| - if (value != null && !value.isEmpty) { |
| - result.write("="); |
| - result.write(Uri.encodeQueryComponent(value)); |
| + if (value == null) { |
|
Søren Gjesse
2015/12/14 07:41:00
Maybe this is OK, but if value is null or the empt
Lasse Reichstein Nielsen
2015/12/14 13:13:12
I think a "foo":[] entry should not introduce a "f
|
| + result.write(Uri.encodeQueryComponent(key)); |
| + } else if (value is String) { |
| + result.write(Uri.encodeQueryComponent(key)); |
| + if (value.isNotEmpty) { |
| + result.write("="); |
| + result.write(Uri.encodeQueryComponent(value)); |
| + } |
| + } else { |
| + Iterable values = value; |
| + for (String value in values) { |
| + result.write(Uri.encodeQueryComponent(key)); |
| + if (value != null && value.isNotEmpty) { |
| + result.write("="); |
| + result.write(Uri.encodeQueryComponent(value)); |
| + } |
| + } |
| } |
| }); |
| return result.toString(); |
| @@ -2156,6 +2202,43 @@ 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) { |
|
floitsch
2015/12/11 19:00:58
new line before and after nested functions.
Lasse Reichstein Nielsen
2016/01/12 13:34:11
Done.
|
| + String key; |
| + String value; |
| + if (equalsIndex < 0) { |
| + key = _uriDecode(encodedComponent, start, end, encoding, true); |
| + value = ""; |
| + } else { |
| + key = |
| + _uriDecode(encodedComponent, start, equalsIndex, encoding, true); |
|
floitsch
2015/12/11 19:00:58
nit: it looks like this would fit on the previous
Lasse Reichstein Nielsen
2016/01/12 13:34:11
It does. Incredible!
|
| + value = |
| + _uriDecode(encodedComponent, equalsIndex + 1, end, encoding, true); |
| + } |
| + result.putIfAbsent(key, _createList).add(value); |
| + } |
| + while (i < query.length) { |
| + int char = query.codeUnitAt(i); |
| + if (char == _equals && equalsIndex < 0) { |
| + equalsIndex = i; |
| + } else if (i == _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). |