| 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..e7e6880b9c89d4f80af199899a1511082274730b 100644
|
| --- a/third_party/WebKit/Source/platform/network/ParsedContentType.cpp
|
| +++ b/third_party/WebKit/Source/platform/network/ParsedContentType.cpp
|
| @@ -31,57 +31,132 @@
|
|
|
| #include "platform/network/ParsedContentType.h"
|
|
|
| -#include "wtf/text/CString.h"
|
| #include "wtf/text/StringBuilder.h"
|
| +#include "wtf/text/StringView.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,
|
| + StringView& output) {
|
| + DCHECK(output.isNull());
|
| +
|
| + while (index < input.length() && input[index] == ' ')
|
| + ++index;
|
| +
|
| + auto start = index;
|
| + while (index < input.length() && isTokenCharacter(mode, input[index]))
|
| + ++index;
|
|
|
| - if (quoted)
|
| - return SubstringRange();
|
| - return SubstringRange(tokenStart, tokenEnd - tokenStart);
|
| + if (start == index)
|
| + return false;
|
| +
|
| + output = StringView(input, start, index - start);
|
| + return true;
|
| }
|
|
|
| -String substringForRange(const String& string, const SubstringRange& range) {
|
| - return string.substring(range.first, range.second);
|
| +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;
|
| +}
|
| +
|
| +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);
|
| + }
|
| + StringView view;
|
| + auto result = consumeToken(mode, input, index, view);
|
| + output = view.toString();
|
| + return result;
|
| +}
|
| +
|
| +bool isEnd(const String& input, unsigned 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 +219,52 @@ 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 << "'";
|
| +
|
| + StringView 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 << ")";
|
| + StringBuilder builder;
|
| + builder.append(type);
|
| + builder.append('/');
|
| + builder.append(subtype);
|
| + m_mimeType = builder.toString();
|
| +
|
| + 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
|
| - << ").";
|
| + StringView key;
|
| + String 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.toString() << "').";
|
| return false;
|
| }
|
| -
|
| - m_parameters.set(substringForRange(contentType, keyRange),
|
| - substringForRange(contentType, valueRange));
|
| - } while (index < contentTypeLength);
|
| -
|
| + map.set(key.toString(), value);
|
| + }
|
| + m_parameters = std::move(map);
|
| return true;
|
| }
|
|
|
|
|