Chromium Code Reviews| Index: third_party/WebKit/Source/platform/network/ParsedContentType.cpp |
| diff --git a/third_party/WebKit/Source/platform/network/ParsedContentType.cpp b/third_party/WebKit/Source/platform/network/ParsedContentType.cpp |
| index b8e7814c017001280345ba7d34ac70f9ee9b863a..de3596b1e2a8cf7e832bade24a41b86a6786a31f 100644 |
| --- a/third_party/WebKit/Source/platform/network/ParsedContentType.cpp |
| +++ b/third_party/WebKit/Source/platform/network/ParsedContentType.cpp |
| @@ -31,57 +31,127 @@ |
| #include "platform/network/ParsedContentType.h" |
| -#include "wtf/text/CString.h" |
| #include "wtf/text/StringBuilder.h" |
| namespace blink { |
| -using SubstringRange = ParsedContentType::SubstringRange; |
| +using Mode = ParsedContentType::Mode; |
| namespace { |
| -void skipSpaces(const String& input, unsigned& startIndex) { |
| - while (startIndex < input.length() && input[startIndex] == ' ') |
| - ++startIndex; |
| +bool isTokenCharacter(Mode mode, UChar c) { |
| + if (c >= 128) |
| + return false; |
| + if (c < 0x20) |
| + return false; |
| + |
| + switch (c) { |
| + case ' ': |
| + case ';': |
| + case '"': |
| + return false; |
| + case '(': |
| + case ')': |
| + case '<': |
| + case '>': |
| + case '@': |
| + case ',': |
| + case ':': |
| + case '\\': |
| + case '/': |
| + case '[': |
| + case ']': |
| + case '?': |
| + case '=': |
| + return mode == Mode::Relaxed; |
| + default: |
| + return true; |
| + } |
| } |
| -SubstringRange parseParameterPart(const String& input, unsigned& startIndex) { |
| - unsigned inputLength = input.length(); |
| - unsigned tokenStart = startIndex; |
| - unsigned& tokenEnd = startIndex; |
| - |
| - if (tokenEnd >= inputLength) |
| - return SubstringRange(); |
| - |
| - bool quoted = input[tokenStart] == '\"'; |
| - bool escape = false; |
| - |
| - while (tokenEnd < inputLength) { |
| - UChar c = input[tokenEnd]; |
| - if (quoted && tokenStart != tokenEnd && c == '\"' && !escape) |
| - return SubstringRange(tokenStart + 1, tokenEnd++ - tokenStart - 1); |
| - if (!quoted && (c == ';' || c == '=')) |
| - return SubstringRange(tokenStart, tokenEnd - tokenStart); |
| - escape = !escape && c == '\\'; |
| - ++tokenEnd; |
| +bool consume(char c, const String& input, unsigned& index) { |
| + DCHECK_NE(c, ' '); |
| + while (index < input.length() && input[index] == ' ') |
| + ++index; |
| + |
| + if (index < input.length() && input[index] == c) { |
| + ++index; |
| + return true; |
| } |
| + return false; |
| +} |
| + |
| +bool consumeToken(Mode mode, |
| + const String& input, |
| + unsigned& index, |
| + String& output) { |
| + DCHECK(output.isNull()); |
| + |
| + while (index < input.length() && input[index] == ' ') |
| + ++index; |
| + |
| + auto start = index; |
| + while (index < input.length() && isTokenCharacter(mode, input[index])) |
| + ++index; |
| + |
| + if (start == index) |
| + return false; |
| + |
| + output = input.substring(start, index - start); |
|
kinuko
2017/02/21 11:43:02
nit: output could be StringView if we want to save
yhirano
2017/02/22 05:17:03
Done.
|
| + return true; |
| +} |
| + |
| +bool consumeQuotedString(const String& input, unsigned& index, String& output) { |
| + StringBuilder builder; |
| + DCHECK_EQ('"', input[index]); |
| + ++index; |
| + while (index < input.length()) { |
| + if (input[index] == '\\') { |
| + ++index; |
| + if (index == input.length()) |
| + return false; |
| + builder.append(input[index]); |
| + ++index; |
| + continue; |
| + } |
| + if (input[index] == '"') { |
| + output = builder.toString(); |
| + ++index; |
| + return true; |
| + } |
| + builder.append(input[index]); |
| + ++index; |
| + } |
| + return false; |
| +} |
| - if (quoted) |
| - return SubstringRange(); |
| - return SubstringRange(tokenStart, tokenEnd - tokenStart); |
| +bool consumeTokenOrQuotedString(Mode mode, |
| + const String& input, |
| + unsigned& index, |
| + String& output) { |
| + while (index < input.length() && input[index] == ' ') |
| + ++index; |
| + if (input.length() == index) |
| + return false; |
| + if (input[index] == '"') |
| + return consumeQuotedString(input, index, output); |
| + return consumeToken(mode, input, index, output); |
| } |
| -String substringForRange(const String& string, const SubstringRange& range) { |
| - return string.substring(range.first, range.second); |
| +bool isEnd(const String& input, unsigned index) { |
|
kinuko
2017/02/21 11:43:02
Any reason this doesn't take unsigned& index?
yhirano
2017/02/22 05:17:03
consume* eat the input and hence update the index.
|
| + while (index < input.length()) { |
| + if (input[index] != ' ') |
| + return false; |
| + ++index; |
| + } |
| + return true; |
| } |
| } // namespace |
| -ParsedContentType::ParsedContentType(const String& contentType) { |
| - if (contentType.contains('\r') || contentType.contains('\n')) |
| - m_isValid = false; |
| - else |
| - m_isValid = parse(contentType.stripWhiteSpace()); |
| +ParsedContentType::ParsedContentType(const String& contentType, Mode mode) |
| + : m_mode(mode) { |
| + m_isValid = parse(contentType); |
| } |
| String ParsedContentType::charset() const { |
| @@ -144,65 +214,47 @@ size_t ParsedContentType::parameterCount() const { |
| bool ParsedContentType::parse(const String& contentType) { |
| unsigned index = 0; |
| - unsigned contentTypeLength = contentType.length(); |
| - skipSpaces(contentType, index); |
| - if (index >= contentTypeLength) { |
| - DVLOG(1) << "Invalid Content-Type string '" << contentType << "'"; |
| + |
| + String type, subtype; |
| + if (!consumeToken(Mode::Normal, contentType, index, type)) { |
| + DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; |
| return false; |
| } |
| - |
| - // There should not be any quoted strings until we reach the parameters. |
| - size_t semiColonIndex = contentType.find(';', index); |
| - if (semiColonIndex == kNotFound) { |
| - m_mimeType = |
| - substringForRange(contentType, |
| - SubstringRange(index, contentTypeLength - index)) |
| - .stripWhiteSpace(); |
| - return true; |
| + if (!consume('/', contentType, index)) { |
| + DVLOG(1) << "Failed to find '/' in '" << contentType << "'"; |
| + return false; |
| + } |
| + if (!consumeToken(Mode::Normal, contentType, index, subtype)) { |
| + DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; |
| + return false; |
| } |
| - m_mimeType = substringForRange(contentType, |
| - SubstringRange(index, semiColonIndex - index)) |
| - .stripWhiteSpace(); |
| - index = semiColonIndex + 1; |
| - do { |
| - skipSpaces(contentType, index); |
| - SubstringRange keyRange = parseParameterPart(contentType, index); |
| - if (!keyRange.second || index >= contentTypeLength) { |
| - DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; |
| + m_mimeType = type + '/' + subtype; |
| + |
| + KeyValuePairs map; |
| + while (!isEnd(contentType, index)) { |
| + if (!consume(';', contentType, index)) { |
| + DVLOG(1) << "Failed to find ';'"; |
| return false; |
| } |
| - // Should we tolerate spaces here? |
| - if (contentType[index++] != '=' || index >= contentTypeLength) { |
| - DVLOG(1) << "Invalid Content-Type malformed parameter (at " << index |
| - << ")."; |
| + String key, value; |
| + if (!consumeToken(Mode::Normal, contentType, index, key)) { |
| + DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; |
| return false; |
| } |
| - |
| - // Should we tolerate spaces here? |
| - SubstringRange valueRange = parseParameterPart(contentType, index); |
| - |
| - if (!valueRange.second) { |
| - DVLOG(1) << "Invalid Content-Type, invalid parameter value (at " << index |
| - << ", for '" |
| - << substringForRange(contentType, keyRange).stripWhiteSpace() |
| - << "')."; |
| + if (!consume('=', contentType, index)) { |
| + DVLOG(1) << "Failed to find '='"; |
| return false; |
| } |
| - |
| - // Should we tolerate spaces here? |
| - if (index < contentTypeLength && contentType[index++] != ';') { |
| - DVLOG(1) << "Invalid Content-Type, invalid character at the end of " |
| - "key/value parameter (at " |
| - << index << ")."; |
| + if (!consumeTokenOrQuotedString(m_mode, contentType, index, value)) { |
| + DVLOG(1) << "Invalid Content-Type, invalid parameter value (at " << index |
| + << ", for '" << key << "')."; |
| return false; |
| } |
| - |
| - m_parameters.set(substringForRange(contentType, keyRange), |
| - substringForRange(contentType, valueRange)); |
| - } while (index < contentTypeLength); |
| - |
| + map.set(key, value); |
| + } |
| + m_parameters = std::move(map); |
| return true; |
| } |