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 else if (name == LinkParameterType) | |
260 m_mimeType = value.lower(); | |
261 else if (name == LinkParameterMedia) | |
262 m_media = value.lower(); | |
263 } | |
264 | |
265 template <typename CharType> | |
266 static void findNextHeader(CharType*& position, CharType* end) | |
267 { | |
268 skipUntil<CharType>(position, end, ','); | |
269 skipExactly<CharType>(position, end, ','); | |
270 } | |
271 | |
272 template <typename CharType> | |
273 LinkHeader::LinkHeader(CharType*& position, CharType* end) | |
274 : m_crossOrigin(CrossOriginAttributeNotSet) | |
275 , m_isValid(true) | |
276 { | |
277 if (!parseURL(position, end, m_url)) { | |
278 m_isValid = false; | |
279 findNextHeader(position, end); | |
280 return; | |
281 } | |
282 | |
283 while (m_isValid && position < end) { | |
284 if (!parseParameterDelimiter(position, end, m_isValid)) { | |
285 findNextHeader(position, end); | |
286 return; | |
287 } | |
288 | |
289 LinkParameterName parameterName; | |
290 if (!parseParameterName(position, end, parameterName)) { | |
291 findNextHeader(position, end); | |
292 m_isValid = false; | |
293 return; | |
294 } | |
295 | |
296 String parameterValue; | |
297 if (!parseParameterValue(position, end, parameterValue) && !isExtensionP
arameter(parameterName)) { | |
298 findNextHeader(position, end); | |
299 m_isValid = false; | |
300 return; | |
301 } | |
302 | |
303 setValue(parameterName, parameterValue); | |
304 } | |
305 findNextHeader(position, end); | |
306 } | |
307 | |
308 LinkHeaderSet::LinkHeaderSet(const String& header) | |
309 { | |
310 if (header.isNull()) | |
311 return; | |
312 | |
313 if (header.is8Bit()) | |
314 init(header.characters8(), header.length()); | |
315 else | |
316 init(header.characters16(), header.length()); | |
317 } | |
318 | |
319 template <typename CharType> | |
320 void LinkHeaderSet::init(CharType* headerValue, unsigned len) | |
321 { | |
322 CharType* position = headerValue; | |
323 CharType* end = headerValue + len; | |
324 while (position < end) | |
325 m_headerSet.append(LinkHeader(position, end)); | |
326 } | |
327 | |
328 } // namespace blink | |
OLD | NEW |