| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "core/loader/LinkHeader.h" | |
| 6 | |
| 7 #include "platform/ParsingUtilities.h" | |
| 8 | |
| 9 namespace blink { | |
| 10 | |
| 11 // LWSP definition in https://www.ietf.org/rfc/rfc0822.txt | |
| 12 template <typename CharType> | |
| 13 static bool isWhitespace(CharType chr) | |
| 14 { | |
| 15 return (chr == ' ') || (chr == '\t'); | |
| 16 } | |
| 17 | |
| 18 template <typename CharType> | |
| 19 static bool isValidURLChar(CharType chr) | |
| 20 { | |
| 21 return !isWhitespace(chr) && chr != '>'; | |
| 22 } | |
| 23 | |
| 24 template <typename CharType> | |
| 25 static bool isValidParameterNameChar(CharType chr) | |
| 26 { | |
| 27 // TODO(yoav): We need to move this function to a central location and possi
bly rewrite as a lookup table. https://crbug.com/527324 | |
| 28 | |
| 29 // Alpha-numeric is a valid char. | |
| 30 // This is likely the common case - bailing early. | |
| 31 if (isASCIIAlphanumeric(chr)) | |
| 32 return true; | |
| 33 // A separator or CTL or '%', '*' or '\'' means the char is not valid. | |
| 34 // So any of: |{}[]/\:;<=>?@,()*'"% | |
| 35 if (chr <= ' ' || chr > '|' || chr == '{' || chr == ']' || chr == '[' | |
| 36 || chr == '/' || chr == '\\' || (chr <= '@' && chr >= ':') || chr == ',' | |
| 37 || (chr >= '(' && chr <= '*') || chr == '\'' || chr == '"' || chr == '%'
) { | |
| 38 return false; | |
| 39 } | |
| 40 return true; | |
| 41 } | |
| 42 | |
| 43 template <typename CharType> | |
| 44 static bool isValidParameterValueEnd(CharType chr) | |
| 45 { | |
| 46 return chr == ';' || chr == ','; | |
| 47 } | |
| 48 | |
| 49 template <typename CharType> | |
| 50 static bool isValidParameterValueChar(CharType chr) | |
| 51 { | |
| 52 return !isWhitespace(chr) && !isValidParameterValueEnd(chr); | |
| 53 } | |
| 54 | |
| 55 // Verify that the parameter is a link-extension which according to spec doesn't
have to have a value. | |
| 56 static bool isExtensionParameter(LinkHeader::LinkParameterName name) | |
| 57 { | |
| 58 return name >= LinkHeader::LinkParameterUnknown; | |
| 59 } | |
| 60 | |
| 61 // Before: | |
| 62 // | |
| 63 // <cat.jpg>; rel=preload | |
| 64 // ^ ^ | |
| 65 // position end | |
| 66 // | |
| 67 // After (if successful: otherwise the method returns false) | |
| 68 // | |
| 69 // <cat.jpg>; rel=preload | |
| 70 // ^ ^ | |
| 71 // position end | |
| 72 template <typename CharType> | |
| 73 static bool parseURL(CharType*& position, CharType* end, String& url) | |
| 74 { | |
| 75 skipWhile<CharType, isWhitespace>(position, end); | |
| 76 if (!skipExactly<CharType>(position, end, '<')) | |
| 77 return false; | |
| 78 skipWhile<CharType, isWhitespace>(position, end); | |
| 79 | |
| 80 CharType* urlStart = position; | |
| 81 skipWhile<CharType, isValidURLChar>(position, end); | |
| 82 CharType* urlEnd = position; | |
| 83 skipUntil<CharType>(position, end, '>'); | |
| 84 if (!skipExactly<CharType>(position, end, '>')) | |
| 85 return false; | |
| 86 | |
| 87 url = String(urlStart, urlEnd - urlStart); | |
| 88 return true; | |
| 89 } | |
| 90 | |
| 91 template <typename CharType> | |
| 92 static bool invalidParameterDelimiter(CharType*& position, CharType* end) | |
| 93 { | |
| 94 return (!skipExactly<CharType>(position, end, ';') && (position < end) && (*
position != ',')); | |
| 95 } | |
| 96 | |
| 97 template <typename CharType> | |
| 98 static bool validFieldEnd(CharType*& position, CharType* end) | |
| 99 { | |
| 100 ASSERT(position <= end); | |
| 101 return (position == end || *position == ','); | |
| 102 } | |
| 103 | |
| 104 // Before: | |
| 105 // | |
| 106 // <cat.jpg>; rel=preload | |
| 107 // ^ ^ | |
| 108 // position end | |
| 109 // | |
| 110 // After (if successful: otherwise the method returns false, and modifies the is
Valid boolean accordingly) | |
| 111 // | |
| 112 // <cat.jpg>; rel=preload | |
| 113 // ^ ^ | |
| 114 // position end | |
| 115 template <typename CharType> | |
| 116 static bool parseParameterDelimiter(CharType*& position, CharType* end, bool& is
Valid) | |
| 117 { | |
| 118 isValid = true; | |
| 119 skipWhile<CharType, isWhitespace>(position, end); | |
| 120 if (invalidParameterDelimiter(position, end)) { | |
| 121 isValid = false; | |
| 122 return false; | |
| 123 } | |
| 124 skipWhile<CharType, isWhitespace>(position, end); | |
| 125 if (validFieldEnd(position, end)) | |
| 126 return false; | |
| 127 return true; | |
| 128 } | |
| 129 | |
| 130 static LinkHeader::LinkParameterName paramterNameFromString(String name) | |
| 131 { | |
| 132 if (equalIgnoringCase(name, "rel")) | |
| 133 return LinkHeader::LinkParameterRel; | |
| 134 if (equalIgnoringCase(name, "anchor")) | |
| 135 return LinkHeader::LinkParameterAnchor; | |
| 136 if (equalIgnoringCase(name, "crossorigin")) | |
| 137 return LinkHeader::LinkParameterCrossOrigin; | |
| 138 if (equalIgnoringCase(name, "title")) | |
| 139 return LinkHeader::LinkParameterTitle; | |
| 140 if (equalIgnoringCase(name, "media")) | |
| 141 return LinkHeader::LinkParameterMedia; | |
| 142 if (equalIgnoringCase(name, "type")) | |
| 143 return LinkHeader::LinkParameterType; | |
| 144 if (equalIgnoringCase(name, "rev")) | |
| 145 return LinkHeader::LinkParameterRev; | |
| 146 if (equalIgnoringCase(name, "hreflang")) | |
| 147 return LinkHeader::LinkParameterHreflang; | |
| 148 if (equalIgnoringCase(name, "as")) | |
| 149 return LinkHeader::LinkParameterAs; | |
| 150 return LinkHeader::LinkParameterUnknown; | |
| 151 } | |
| 152 | |
| 153 // Before: | |
| 154 // | |
| 155 // <cat.jpg>; rel=preload | |
| 156 // ^ ^ | |
| 157 // position end | |
| 158 // | |
| 159 // After (if successful: otherwise the method returns false) | |
| 160 // | |
| 161 // <cat.jpg>; rel=preload | |
| 162 // ^ ^ | |
| 163 // position end | |
| 164 template <typename CharType> | |
| 165 static bool parseParameterName(CharType*& position, CharType* end, LinkHeader::L
inkParameterName& name) | |
| 166 { | |
| 167 CharType* nameStart = position; | |
| 168 skipWhile<CharType, isValidParameterNameChar>(position, end); | |
| 169 CharType* nameEnd = position; | |
| 170 skipWhile<CharType, isWhitespace>(position, end); | |
| 171 bool hasEqual = skipExactly<CharType>(position, end, '='); | |
| 172 skipWhile<CharType, isWhitespace>(position, end); | |
| 173 name = paramterNameFromString(String(nameStart, nameEnd - nameStart)); | |
| 174 if (hasEqual) | |
| 175 return true; | |
| 176 bool validParameterValueEnd = (position == end) || isValidParameterValueEnd(
*position); | |
| 177 return validParameterValueEnd && isExtensionParameter(name); | |
| 178 } | |
| 179 | |
| 180 // Before: | |
| 181 // | |
| 182 // <cat.jpg>; rel="preload"; type="image/jpeg"; | |
| 183 // ^ ^ | |
| 184 // position end | |
| 185 // | |
| 186 // After (if the parameter starts with a quote, otherwise the method returns fal
se) | |
| 187 // | |
| 188 // <cat.jpg>; rel="preload"; type="image/jpeg"; | |
| 189 // ^ ^ | |
| 190 // position end | |
| 191 template <typename CharType> | |
| 192 static bool skipQuotesIfNeeded(CharType*& position, CharType* end, bool& complet
eQuotes) | |
| 193 { | |
| 194 ASSERT(position <= end); | |
| 195 unsigned char quote; | |
| 196 completeQuotes = false; | |
| 197 if (skipExactly<CharType>(position, end, '\'')) | |
| 198 quote = '\''; | |
| 199 else if (skipExactly<CharType>(position, end, '"')) | |
| 200 quote = '"'; | |
| 201 else | |
| 202 return false; | |
| 203 | |
| 204 while (!completeQuotes && position < end) { | |
| 205 skipUntil(position, end, static_cast<CharType>(quote)); | |
| 206 if (*(position - 1) != '\\') | |
| 207 completeQuotes = true; | |
| 208 completeQuotes = skipExactly(position, end, static_cast<CharType>(quote)
) && completeQuotes; | |
| 209 } | |
| 210 return true; | |
| 211 } | |
| 212 | |
| 213 // Before: | |
| 214 // | |
| 215 // <cat.jpg>; rel=preload; foo=bar | |
| 216 // ^ ^ | |
| 217 // position end | |
| 218 // | |
| 219 // After (if successful: otherwise the method returns false) | |
| 220 // | |
| 221 // <cat.jpg>; rel=preload; foo=bar | |
| 222 // ^ ^ | |
| 223 // position end | |
| 224 template <typename CharType> | |
| 225 static bool parseParameterValue(CharType*& position, CharType* end, String& valu
e) | |
| 226 { | |
| 227 CharType* valueStart = position; | |
| 228 CharType* valueEnd = position; | |
| 229 bool completeQuotes; | |
| 230 bool hasQuotes = skipQuotesIfNeeded(position, end, completeQuotes); | |
| 231 if (!hasQuotes) { | |
| 232 skipWhile<CharType, isValidParameterValueChar>(position, end); | |
| 233 } | |
| 234 valueEnd = position; | |
| 235 skipWhile<CharType, isWhitespace>(position, end); | |
| 236 if ((!completeQuotes && valueStart == valueEnd) || (position != end && !isVa
lidParameterValueEnd(*position))) { | |
| 237 value = String(""); | |
| 238 return false; | |
| 239 } | |
| 240 if (hasQuotes) | |
| 241 ++valueStart; | |
| 242 if (completeQuotes) | |
| 243 --valueEnd; | |
| 244 value = String(valueStart, valueEnd - valueStart); | |
| 245 return !hasQuotes || completeQuotes; | |
| 246 } | |
| 247 | |
| 248 void LinkHeader::setValue(LinkParameterName name, String value) | |
| 249 { | |
| 250 // FIXME: Add support for more header parameters as neccessary. | |
| 251 if (name == LinkParameterRel && !m_rel) | |
| 252 m_rel = value.lower(); | |
| 253 else if (name == LinkParameterAnchor) | |
| 254 m_isValid = false; | |
| 255 else if (name == LinkParameterCrossOrigin) | |
| 256 m_crossOrigin = crossOriginAttributeValue(value); | |
| 257 else if (name == LinkParameterAs) | |
| 258 m_as = value.lower(); | |
| 259 } | |
| 260 | |
| 261 template <typename CharType> | |
| 262 static void findNextHeader(CharType*& position, CharType* end) | |
| 263 { | |
| 264 skipUntil<CharType>(position, end, ','); | |
| 265 skipExactly<CharType>(position, end, ','); | |
| 266 } | |
| 267 | |
| 268 template <typename CharType> | |
| 269 LinkHeader::LinkHeader(CharType*& position, CharType* end) | |
| 270 : m_crossOrigin(CrossOriginAttributeNotSet) | |
| 271 , m_isValid(true) | |
| 272 { | |
| 273 if (!parseURL(position, end, m_url)) { | |
| 274 m_isValid = false; | |
| 275 findNextHeader(position, end); | |
| 276 return; | |
| 277 } | |
| 278 | |
| 279 while (m_isValid && position < end) { | |
| 280 if (!parseParameterDelimiter(position, end, m_isValid)) { | |
| 281 findNextHeader(position, end); | |
| 282 return; | |
| 283 } | |
| 284 | |
| 285 LinkParameterName parameterName; | |
| 286 if (!parseParameterName(position, end, parameterName)) { | |
| 287 findNextHeader(position, end); | |
| 288 m_isValid = false; | |
| 289 return; | |
| 290 } | |
| 291 | |
| 292 String parameterValue; | |
| 293 if (!parseParameterValue(position, end, parameterValue) && !isExtensionP
arameter(parameterName)) { | |
| 294 findNextHeader(position, end); | |
| 295 m_isValid = false; | |
| 296 return; | |
| 297 } | |
| 298 | |
| 299 setValue(parameterName, parameterValue); | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 LinkHeaderSet::LinkHeaderSet(const String& header) | |
| 304 { | |
| 305 if (header.isNull()) | |
| 306 return; | |
| 307 | |
| 308 if (header.is8Bit()) | |
| 309 init(header.characters8(), header.length()); | |
| 310 else | |
| 311 init(header.characters16(), header.length()); | |
| 312 } | |
| 313 | |
| 314 template <typename CharType> | |
| 315 void LinkHeaderSet::init(CharType* headerValue, unsigned len) | |
| 316 { | |
| 317 CharType* position = headerValue; | |
| 318 CharType* end = headerValue + len; | |
| 319 while (position < end) | |
| 320 m_headerSet.append(LinkHeader(position, end)); | |
| 321 } | |
| 322 | |
| 323 } // namespace blink | |
| OLD | NEW |