| 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 /** | 7 /** |
| 8 * A parsed URI, such as a URL. | 8 * A parsed URI, such as a URL. |
| 9 * | 9 * |
| 10 * **See also:** | 10 * **See also:** |
| (...skipping 663 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 674 if (result == null) { | 674 if (result == null) { |
| 675 assert(prevIndex == 0); | 675 assert(prevIndex == 0); |
| 676 result = new StringBuffer(component.substring(prevIndex, index)); | 676 result = new StringBuffer(component.substring(prevIndex, index)); |
| 677 } else { | 677 } else { |
| 678 result.write(component.substring(prevIndex, index)); | 678 result.write(component.substring(prevIndex, index)); |
| 679 } | 679 } |
| 680 } | 680 } |
| 681 | 681 |
| 682 while (index < length) { | 682 while (index < length) { |
| 683 | 683 |
| 684 // Normalize percent encoding to uppercase and don't encode | 684 // Normalize percent-encoding to uppercase and don't encode |
| 685 // unreserved characters. | 685 // unreserved characters. |
| 686 if (component.codeUnitAt(index) == _PERCENT) { | 686 if (component.codeUnitAt(index) == _PERCENT) { |
| 687 if (length < index + 2) { | 687 if (length < index + 2) { |
| 688 throw new ArgumentError( | 688 throw new ArgumentError( |
| 689 "Invalid percent-encoding in URI component: $component"); | 689 "Invalid percent-encoding in URI component: $component"); |
| 690 } | 690 } |
| 691 | 691 |
| 692 var codeUnit1 = component.codeUnitAt(index + 1); | 692 var codeUnit1 = component.codeUnitAt(index + 1); |
| 693 var codeUnit2 = component.codeUnitAt(index + 2); | 693 var codeUnit2 = component.codeUnitAt(index + 2); |
| 694 var decodedCodeUnit = decodeHexDigitPair(index + 1); | 694 var decodedCodeUnit = decodeHexDigitPair(index + 1); |
| (...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1107 */ | 1107 */ |
| 1108 static String encodeComponent(String component) { | 1108 static String encodeComponent(String component) { |
| 1109 return _uriEncode(_unreserved2396Table, component); | 1109 return _uriEncode(_unreserved2396Table, component); |
| 1110 } | 1110 } |
| 1111 | 1111 |
| 1112 /** | 1112 /** |
| 1113 * Encode the string [component] according to the HTML 4.01 rules | 1113 * Encode the string [component] according to the HTML 4.01 rules |
| 1114 * for encoding the posting of a HTML form as a query string | 1114 * for encoding the posting of a HTML form as a query string |
| 1115 * component. | 1115 * component. |
| 1116 * | 1116 * |
| 1117 * Spaces will be replaced with plus and all characters except for | 1117 * Encode the string [component] according to the HTML 4.01 rules |
| 1118 * uppercase and lowercase letters, decimal digits and the | 1118 * for encoding the posting of a HTML form as a query string |
| 1119 * characters `-._~`. Note that the set of characters encoded is a | 1119 * component. |
| 1120 * superset of what HTML 4.01 says as it refers to RFC 1738 for | 1120 |
| 1121 * reserved characters. | 1121 * The component is first encoded to bytes using [encoding]. |
| 1122 * The default is to use [UTF8] encoding, which preserves all |
| 1123 * the characters that don't need encoding. |
| 1124 |
| 1125 * Then the resulting bytes are "percent-encoded". This transforms |
| 1126 * spaces (U+0020) to a plus sign ('+') and all bytes that are not |
| 1127 * the ASCII decimal digits, letters or one of '-._~' are written as |
| 1128 * a percent sign '%' followed by the two-digit hexadecimal |
| 1129 * representation of the byte. |
| 1130 |
| 1131 * Note that the set of characters which are percent-encoded is a |
| 1132 * superset of what HTML 4.01 requires, since it refers to RFC 1738 |
| 1133 * for reserved characters. |
| 1122 * | 1134 * |
| 1123 * When manually encoding query components remember to encode each | 1135 * When manually encoding query components remember to encode each |
| 1124 * part separately before building the query string. | 1136 * part separately before building the query string. |
| 1125 * | 1137 * |
| 1126 * To avoid the need for explicitly encoding the query use the | 1138 * To avoid the need for explicitly encoding the query use the |
| 1127 * [queryParameters] optional named arguments when constructing a | 1139 * [queryParameters] optional named arguments when constructing a |
| 1128 * [Uri]. | 1140 * [Uri]. |
| 1129 * | 1141 * |
| 1130 * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more | 1142 * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more |
| 1131 * details. | 1143 * details. |
| 1132 */ | 1144 */ |
| 1133 static String encodeQueryComponent(String component) { | 1145 static String encodeQueryComponent(String component, |
| 1134 return _uriEncode(_unreservedTable, component, spaceToPlus: true); | 1146 {Encoding encoding: UTF8}) { |
| 1147 return _uriEncode( |
| 1148 _unreservedTable, component, encoding: encoding, spaceToPlus: true); |
| 1135 } | 1149 } |
| 1136 | 1150 |
| 1137 /** | 1151 /** |
| 1138 * Decodes the percent-encoding in [encodedComponent]. | 1152 * Decodes the percent-encoding in [encodedComponent]. |
| 1139 * | 1153 * |
| 1140 * Note that decoding a URI component might change its meaning as | 1154 * Note that decoding a URI component might change its meaning as |
| 1141 * some of the decoded characters could be characters with are | 1155 * some of the decoded characters could be characters with are |
| 1142 * delimiters for a given URI componene type. Always split a URI | 1156 * delimiters for a given URI componene type. Always split a URI |
| 1143 * component using the delimiters for the component before decoding | 1157 * component using the delimiters for the component before decoding |
| 1144 * the individual parts. | 1158 * the individual parts. |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1346 if (value == -1) { | 1360 if (value == -1) { |
| 1347 return new List.filled((9 - parts.length) * 2, 0); | 1361 return new List.filled((9 - parts.length) * 2, 0); |
| 1348 } else { | 1362 } else { |
| 1349 return [(value >> 8) & 0xFF, value & 0xFF]; | 1363 return [(value >> 8) & 0xFF, value & 0xFF]; |
| 1350 } | 1364 } |
| 1351 }) | 1365 }) |
| 1352 .toList(); | 1366 .toList(); |
| 1353 } | 1367 } |
| 1354 | 1368 |
| 1355 // Frequently used character codes. | 1369 // Frequently used character codes. |
| 1370 static const int _SPACE = 0x20; |
| 1356 static const int _DOUBLE_QUOTE = 0x22; | 1371 static const int _DOUBLE_QUOTE = 0x22; |
| 1357 static const int _PERCENT = 0x25; | 1372 static const int _PERCENT = 0x25; |
| 1358 static const int _ASTERISK = 0x2A; | 1373 static const int _ASTERISK = 0x2A; |
| 1359 static const int _PLUS = 0x2B; | 1374 static const int _PLUS = 0x2B; |
| 1360 static const int _SLASH = 0x2F; | 1375 static const int _SLASH = 0x2F; |
| 1361 static const int _ZERO = 0x30; | 1376 static const int _ZERO = 0x30; |
| 1362 static const int _NINE = 0x39; | 1377 static const int _NINE = 0x39; |
| 1363 static const int _COLON = 0x3A; | 1378 static const int _COLON = 0x3A; |
| 1364 static const int _LESS = 0x3C; | 1379 static const int _LESS = 0x3C; |
| 1365 static const int _GREATER = 0x3E; | 1380 static const int _GREATER = 0x3E; |
| 1366 static const int _QUESTION = 0x3F; | 1381 static const int _QUESTION = 0x3F; |
| 1367 static const int _AT_SIGN = 0x40; | 1382 static const int _AT_SIGN = 0x40; |
| 1368 static const int _UPPER_CASE_A = 0x41; | 1383 static const int _UPPER_CASE_A = 0x41; |
| 1369 static const int _UPPER_CASE_F = 0x46; | 1384 static const int _UPPER_CASE_F = 0x46; |
| 1370 static const int _UPPER_CASE_Z = 0x5A; | 1385 static const int _UPPER_CASE_Z = 0x5A; |
| 1371 static const int _LEFT_BRACKET = 0x5B; | 1386 static const int _LEFT_BRACKET = 0x5B; |
| 1372 static const int _BACKSLASH = 0x5C; | 1387 static const int _BACKSLASH = 0x5C; |
| 1373 static const int _RIGHT_BRACKET = 0x5D; | 1388 static const int _RIGHT_BRACKET = 0x5D; |
| 1374 static const int _LOWER_CASE_A = 0x61; | 1389 static const int _LOWER_CASE_A = 0x61; |
| 1375 static const int _LOWER_CASE_F = 0x66; | 1390 static const int _LOWER_CASE_F = 0x66; |
| 1376 static const int _LOWER_CASE_Z = 0x7A; | 1391 static const int _LOWER_CASE_Z = 0x7A; |
| 1377 static const int _BAR = 0x7C; | 1392 static const int _BAR = 0x7C; |
| 1378 | 1393 |
| 1379 /** | 1394 /** |
| 1380 * This is the internal implementation of JavaScript's encodeURI function. | 1395 * This is the internal implementation of JavaScript's encodeURI function. |
| 1381 * It encodes all characters in the string [text] except for those | 1396 * It encodes all characters in the string [text] except for those |
| 1382 * that appear in [canonicalTable], and returns the escaped string. | 1397 * that appear in [canonicalTable], and returns the escaped string. |
| 1383 */ | 1398 */ |
| 1384 static String _uriEncode(List<int> canonicalTable, | 1399 static String _uriEncode(List<int> canonicalTable, |
| 1385 String text, | 1400 String text, |
| 1386 {bool spaceToPlus: false}) { | 1401 {Encoding encoding: UTF8, |
| 1387 byteToHex(int v) { | 1402 bool spaceToPlus: false}) { |
| 1388 final String hex = '0123456789ABCDEF'; | 1403 byteToHex(byte, buffer) { |
| 1389 return '%${hex[v >> 4]}${hex[v & 0x0f]}'; | 1404 const String hex = '0123456789ABCDEF'; |
| 1405 buffer.writeCharCode(hex.codeUnitAt(byte >> 4)); |
| 1406 buffer.writeCharCode(hex.codeUnitAt(byte & 0x0f)); |
| 1390 } | 1407 } |
| 1391 | 1408 |
| 1409 // Encode the string into bytes then generate an ASCII only string |
| 1410 // by percent encoding selected bytes. |
| 1392 StringBuffer result = new StringBuffer(); | 1411 StringBuffer result = new StringBuffer(); |
| 1393 for (int i = 0; i < text.length; i++) { | 1412 var bytes = encoding.encode(text); |
| 1394 int ch = text.codeUnitAt(i); | 1413 for (int i = 0; i < bytes.length; i++) { |
| 1395 if (ch < 128 && ((canonicalTable[ch >> 4] & (1 << (ch & 0x0f))) != 0)) { | 1414 int byte = bytes[i]; |
| 1396 result.write(text[i]); | 1415 if (byte < 128 && |
| 1397 } else if (spaceToPlus && text[i] == " ") { | 1416 ((canonicalTable[byte >> 4] & (1 << (byte & 0x0f))) != 0)) { |
| 1398 result.write("+"); | 1417 result.writeCharCode(byte); |
| 1418 } else if (spaceToPlus && byte == _SPACE) { |
| 1419 result.writeCharCode(_PLUS); |
| 1399 } else { | 1420 } else { |
| 1400 if (ch >= 0xD800 && ch < 0xDC00) { | 1421 result.writeCharCode(_PERCENT); |
| 1401 // Low surrogate. We expect a next char high surrogate. | 1422 byteToHex(byte, result); |
| 1402 ++i; | |
| 1403 int nextCh = text.length == i ? 0 : text.codeUnitAt(i); | |
| 1404 if (nextCh >= 0xDC00 && nextCh < 0xE000) { | |
| 1405 // convert the pair to a U+10000 codepoint | |
| 1406 ch = 0x10000 + ((ch - 0xD800) << 10) + (nextCh - 0xDC00); | |
| 1407 } else { | |
| 1408 throw new ArgumentError('Malformed URI'); | |
| 1409 } | |
| 1410 } | |
| 1411 // TODO(floitsch): don't allocate a new string. | |
| 1412 for (int codepoint in UTF8.encode(new String.fromCharCode(ch))) { | |
| 1413 result.write(byteToHex(codepoint)); | |
| 1414 } | |
| 1415 } | 1423 } |
| 1416 } | 1424 } |
| 1417 return result.toString(); | 1425 return result.toString(); |
| 1418 } | 1426 } |
| 1419 | 1427 |
| 1420 /** | 1428 /** |
| 1421 * Convert a byte (2 character hex sequence) in string [s] starting | 1429 * Convert a byte (2 character hex sequence) in string [s] starting |
| 1422 * at position [pos] to its ordinal value | 1430 * at position [pos] to its ordinal value |
| 1423 */ | 1431 */ |
| 1424 static int _hexCharPairToByte(String s, int pos) { | 1432 static int _hexCharPairToByte(String s, int pos) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1448 * This function is similar to the JavaScript-function `decodeURI`. | 1456 * This function is similar to the JavaScript-function `decodeURI`. |
| 1449 * | 1457 * |
| 1450 * If [plusToSpace] is `true`, plus characters will be converted to spaces. | 1458 * If [plusToSpace] is `true`, plus characters will be converted to spaces. |
| 1451 * | 1459 * |
| 1452 * The decoder will create a byte-list of the percent-encoded parts, and then | 1460 * The decoder will create a byte-list of the percent-encoded parts, and then |
| 1453 * decode the byte-list using [encoding]. The default encodingis UTF-8. | 1461 * decode the byte-list using [encoding]. The default encodingis UTF-8. |
| 1454 */ | 1462 */ |
| 1455 static String _uriDecode(String text, | 1463 static String _uriDecode(String text, |
| 1456 {bool plusToSpace: false, | 1464 {bool plusToSpace: false, |
| 1457 Encoding encoding: UTF8}) { | 1465 Encoding encoding: UTF8}) { |
| 1458 StringBuffer result = new StringBuffer(); | 1466 // First check whether there is any characters which need special handling. |
| 1459 List<int> codepoints = new List<int>(); | 1467 bool simple = true; |
| 1460 for (int i = 0; i < text.length;) { | 1468 for (int i = 0; i < text.length && simple; i++) { |
| 1461 int ch = text.codeUnitAt(i); | 1469 var codeUnit = text.codeUnitAt(i); |
| 1462 if (ch != _PERCENT) { | 1470 simple = codeUnit != _PERCENT && codeUnit != _PLUS; |
| 1463 if (plusToSpace && ch == _PLUS) { | 1471 } |
| 1464 result.write(" "); | 1472 List<int> bytes; |
| 1465 } else { | 1473 if (simple) { |
| 1466 result.writeCharCode(ch); | 1474 if (encoding == UTF8 || encoding == LATIN1) { |
| 1475 return text; |
| 1476 } else { |
| 1477 bytes = text.codeUnits; |
| 1478 } |
| 1479 } else { |
| 1480 bytes = new List(); |
| 1481 for (int i = 0; i < text.length; i++) { |
| 1482 var codeUnit = text.codeUnitAt(i); |
| 1483 if (codeUnit > 127) { |
| 1484 throw new ArgumentError("Illegal percent encoding in URI"); |
| 1467 } | 1485 } |
| 1468 i++; | 1486 if (codeUnit == _PERCENT) { |
| 1469 } else { | 1487 if (i + 3 > text.length) { |
| 1470 codepoints.clear(); | |
| 1471 while (ch == _PERCENT) { | |
| 1472 if (++i > text.length - 2) { | |
| 1473 throw new ArgumentError('Truncated URI'); | 1488 throw new ArgumentError('Truncated URI'); |
| 1474 } | 1489 } |
| 1475 codepoints.add(_hexCharPairToByte(text, i)); | 1490 bytes.add(_hexCharPairToByte(text, i + 1)); |
| 1476 i += 2; | 1491 i += 2; |
| 1477 if (i == text.length) break; | 1492 } else if (plusToSpace && codeUnit == _PLUS) { |
| 1478 ch = text.codeUnitAt(i); | 1493 bytes.add(_SPACE); |
| 1494 } else { |
| 1495 bytes.add(codeUnit); |
| 1479 } | 1496 } |
| 1480 result.write(encoding.decode(codepoints)); | |
| 1481 } | 1497 } |
| 1482 } | 1498 } |
| 1483 return result.toString(); | 1499 return encoding.decode(bytes); |
| 1484 } | 1500 } |
| 1485 | 1501 |
| 1486 static bool _isAlphabeticCharacter(int codeUnit) | 1502 static bool _isAlphabeticCharacter(int codeUnit) |
| 1487 => (codeUnit >= _LOWER_CASE_A && codeUnit <= _LOWER_CASE_Z) || | 1503 => (codeUnit >= _LOWER_CASE_A && codeUnit <= _LOWER_CASE_Z) || |
| 1488 (codeUnit >= _UPPER_CASE_A && codeUnit <= _UPPER_CASE_Z); | 1504 (codeUnit >= _UPPER_CASE_A && codeUnit <= _UPPER_CASE_Z); |
| 1489 | 1505 |
| 1490 // Tables of char-codes organized as a bit vector of 128 bits where | 1506 // Tables of char-codes organized as a bit vector of 128 bits where |
| 1491 // each bit indicate whether a character code on the 0-127 needs to | 1507 // each bit indicate whether a character code on the 0-127 needs to |
| 1492 // be escaped or not. | 1508 // be escaped or not. |
| 1493 | 1509 |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1674 void clear() { | 1690 void clear() { |
| 1675 throw new UnsupportedError("Cannot modify an unmodifiable map"); | 1691 throw new UnsupportedError("Cannot modify an unmodifiable map"); |
| 1676 } | 1692 } |
| 1677 void forEach(void f(K key, V value)) => _map.forEach(f); | 1693 void forEach(void f(K key, V value)) => _map.forEach(f); |
| 1678 Iterable<K> get keys => _map.keys; | 1694 Iterable<K> get keys => _map.keys; |
| 1679 Iterable<V> get values => _map.values; | 1695 Iterable<V> get values => _map.values; |
| 1680 int get length => _map.length; | 1696 int get length => _map.length; |
| 1681 bool get isEmpty => _map.isEmpty; | 1697 bool get isEmpty => _map.isEmpty; |
| 1682 bool get isNotEmpty => _map.isNotEmpty; | 1698 bool get isNotEmpty => _map.isNotEmpty; |
| 1683 } | 1699 } |
| OLD | NEW |