Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(164)

Side by Side Diff: Source/core/html/parser/HTMLSrcsetParser.cpp

Issue 293423002: Refactor srcset parser to align it with spec changes (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Review comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 19 matching lines...) Expand all
30 30
31 #include "config.h" 31 #include "config.h"
32 #include "core/html/parser/HTMLSrcsetParser.h" 32 #include "core/html/parser/HTMLSrcsetParser.h"
33 33
34 #include "RuntimeEnabledFeatures.h" 34 #include "RuntimeEnabledFeatures.h"
35 #include "core/html/parser/HTMLParserIdioms.h" 35 #include "core/html/parser/HTMLParserIdioms.h"
36 #include "platform/ParsingUtilities.h" 36 #include "platform/ParsingUtilities.h"
37 37
38 namespace WebCore { 38 namespace WebCore {
39 39
40 static bool compareByScaleFactor(const ImageCandidate& first, const ImageCandida te& second) 40 static bool compareByDensity(const ImageCandidate& first, const ImageCandidate& second)
41 { 41 {
42 return first.scaleFactor() < second.scaleFactor(); 42 return first.density() < second.density();
43 }
44
45 enum DescriptorTokenizerState {
46 Start,
47 InParenthesis,
48 AfterToken,
49 };
50
51 struct DescriptorToken {
52 unsigned start;
53 unsigned length;
54 DescriptorToken(unsigned start, unsigned length)
55 : start(start)
56 , length(length)
57 {
58 }
59 };
60
61 template<typename CharType>
62 static void appendDescriptorAndReset(const CharType* attributeStart, const CharT ype*& descriptorStart, const CharType* position, Vector<DescriptorToken>& descri ptors)
63 {
64 if (position > descriptorStart)
65 descriptors.append(DescriptorToken(descriptorStart - attributeStart, pos ition - descriptorStart));
66 descriptorStart = 0;
67 }
68
69 // The following is called appendCharacter to match the spec's terminology.
70 template<typename CharType>
71 static void appendCharacter(const CharType* descriptorStart, const CharType* pos ition)
72 {
73 // Since we don't copy the tokens, this just set the point where the descrip tor tokens start.
74 if (!descriptorStart)
75 descriptorStart = position;
43 } 76 }
44 77
45 template<typename CharType> 78 template<typename CharType>
46 inline bool isComma(CharType character) 79 static bool isEOF(const CharType* position, const CharType* end)
47 { 80 {
48 return character == ','; 81 return position >= end;
49 } 82 }
50 83
51 template<typename CharType> 84 template<typename CharType>
52 static bool parseDescriptors(const CharType* descriptorsStart, const CharType* d escriptorsEnd, DescriptorParsingResult& result) 85 static void tokenizeDescriptors(const CharType* attributeStart,
86 const CharType*& position,
87 const CharType* attributeEnd,
88 Vector<DescriptorToken>& descriptors)
53 { 89 {
54 const CharType* position = descriptorsStart; 90 DescriptorTokenizerState state = Start;
55 bool isValid = false; 91 const CharType* descriptorsStart = position;
56 bool isEmptyDescriptor = !(descriptorsEnd > descriptorsStart); 92 const CharType* currentDescriptorStart = descriptorsStart;
57 while (position < descriptorsEnd) { 93 while (true) {
58 // 13.1. Let descriptor list be the result of splitting unparsed descrip tors on spaces. 94 switch (state) {
59 skipWhile<CharType, isHTMLSpace<CharType> >(position, descriptorsEnd); 95 case Start:
60 const CharType* currentDescriptorStart = position; 96 if (isEOF(position, attributeEnd)) {
61 skipWhile<CharType, isNotHTMLSpace<CharType> >(position, descriptorsEnd) ; 97 appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors);
62 const CharType* currentDescriptorEnd = position; 98 return;
99 }
100 if (isComma(*position)) {
101 appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors);
102 ++position;
103 return;
104 }
105 if (isHTMLSpace(*position)) {
106 appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors);
107 currentDescriptorStart = position + 1;
108 state = AfterToken;
109 } else if (*position == '(') {
110 appendCharacter(currentDescriptorStart, position);
111 state = InParenthesis;
112 } else {
113 appendCharacter(currentDescriptorStart, position);
114 }
115 break;
116 case InParenthesis:
117 if (isEOF(position, attributeEnd)) {
118 appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors);
119 return;
120 }
121 if (*position == ')') {
122 appendCharacter(currentDescriptorStart, position);
123 state = Start;
124 } else {
125 appendCharacter(currentDescriptorStart, position);
126 }
127 break;
128 case AfterToken:
129 if (isEOF(position, attributeEnd))
130 return;
131 if (!isHTMLSpace(*position)) {
132 state = Start;
133 currentDescriptorStart = position;
134 --position;
135 }
136 break;
137 }
138 ++position;
139 }
140 }
63 141
64 ++position; 142 template<typename CharType>
65 ASSERT(currentDescriptorEnd > currentDescriptorStart); 143 static bool parseDescriptors(const CharType* attribute, Vector<DescriptorToken>& descriptors, DescriptorParsingResult& result)
66 --currentDescriptorEnd; 144 {
67 unsigned descriptorLength = currentDescriptorEnd - currentDescriptorStar t; 145 for (Vector<DescriptorToken>::iterator it = descriptors.begin(); it != descr iptors.end(); ++it) {
68 if (*currentDescriptorEnd == 'x') { 146 if (it->length == 0)
69 if (result.foundDescriptor()) 147 continue;
148 CharType c = attribute[it->start + it->length - 1];
eseidel 2014/05/29 06:59:53 Should DescriptorToken have a helper for this valu
149 bool isValid = false;
150 if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'w') {
151 if (result.hasDensity() || result.hasWidth())
70 return false; 152 return false;
71 result.scaleFactor = charactersToFloat(currentDescriptorStart, descr iptorLength, &isValid); 153 int resourceWidth = charactersToInt(attribute + it->start, it->lengt h - 1, &isValid);
eseidel 2014/05/29 06:59:53 Should charactersToInt be a member on DescriptorTo
72 if (!isValid || result.scaleFactor < 0) 154 if (!isValid || resourceWidth <= 0)
73 return false; 155 return false;
74 } else if (RuntimeEnabledFeatures::pictureSizesEnabled() && *currentDesc riptorEnd == 'w') { 156 result.setResourceWidth(resourceWidth);
75 if (result.foundDescriptor()) 157 } else if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'h') {
158 // This is here only for future compat purposes.
159 // The value of the 'h' descriptor is not used.
160 if (result.hasDensity() || result.hasHeight())
76 return false; 161 return false;
77 result.resourceWidth = charactersToInt(currentDescriptorStart, descr iptorLength, &isValid); 162 int resourceHeight = charactersToInt(attribute + it->start, it->leng th - 1, &isValid);
78 if (!isValid || result.resourceWidth <= 0) 163 if (!isValid || resourceHeight <= 0)
79 return false; 164 return false;
165 result.setResourceHeight(resourceHeight);
166 } else if (c == 'x') {
167 if (result.hasDensity() || result.hasHeight() || result.hasWidth())
168 return false;
169 int density = charactersToFloat(attribute + it->start, it->length - 1, &isValid);
170 if (!isValid || density < 0)
171 return false;
172 result.setDensity(density);
80 } 173 }
81 } 174 }
82 if (isEmptyDescriptor) 175 return true;
83 result.scaleFactor = 1.0;
84 return result.foundDescriptor();
85 } 176 }
86 177
87 // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content- 1.html#processing-the-image-candidates 178 static bool parseDescriptors(const String& attribute, Vector<DescriptorToken>& d escriptors, DescriptorParsingResult& result)
179 {
180 // FIXME: See if StringView can't be extended to replace DescriptorToken her e.
181 if (attribute.is8Bit()) {
182 return parseDescriptors(attribute.characters8(), descriptors, result);
183 }
184 return parseDescriptors(attribute.characters16(), descriptors, result);
185 }
186
187 // http://picture.responsiveimages.org/#parse-srcset-attr
88 template<typename CharType> 188 template<typename CharType>
89 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, con st CharType* attributeStart, unsigned length, Vector<ImageCandidate>& imageCandi dates) 189 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, con st CharType* attributeStart, unsigned length, Vector<ImageCandidate>& imageCandi dates)
90 { 190 {
91 const CharType* position = attributeStart; 191 const CharType* position = attributeStart;
92 const CharType* attributeEnd = position + length; 192 const CharType* attributeEnd = position + length;
93 193
94 while (position < attributeEnd) { 194 while (position < attributeEnd) {
95 DescriptorParsingResult result; 195 // 4. Splitting loop: Collect a sequence of characters that are space ch aracters or U+002C COMMA characters.
96 // 4. Splitting loop: Skip whitespace. 196 skipWhile<CharType, isHTMLSpaceOrComma<CharType> >(position, attributeEn d);
97 skipWhile<CharType, isHTMLSpace<CharType> >(position, attributeEnd); 197 if (position == attributeEnd) {
98 if (position == attributeEnd) 198 // Contrary to spec language - descriptor parsing happens on each ca ndidate, so when we reach the attributeEnd, we can exit.
99 break; 199 break;
200 }
100 const CharType* imageURLStart = position; 201 const CharType* imageURLStart = position;
202 // 6. Collect a sequence of characters that are not space characters, an d let that be url.
101 203
102 // If The current candidate is either totally empty or only contains spa ce, skipping.
103 if (*position == ',') {
104 ++position;
105 continue;
106 }
107
108 // 5. Collect a sequence of characters that are not space characters, an d let that be url.
109 skipUntil<CharType, isHTMLSpace<CharType> >(position, attributeEnd); 204 skipUntil<CharType, isHTMLSpace<CharType> >(position, attributeEnd);
110 const CharType* imageURLEnd = position; 205 const CharType* imageURLEnd = position;
111 206
112 if (position != attributeEnd && *(position - 1) == ',') { 207 DescriptorParsingResult result;
113 --imageURLEnd; 208
114 result.scaleFactor = 1.0; 209 // 8. If url ends with a U+002C COMMA character (,)
210 if (isComma(*(position - 1))) {
211 // Remove all trailing U+002C COMMA characters from url.
212 imageURLEnd = position - 1;
213 reverseSkipWhile<CharType, isComma>(imageURLEnd, imageURLStart);
214 ++imageURLEnd;
215 // If url is empty, then jump to the step labeled splitting loop.
216 if (imageURLStart == imageURLEnd)
217 continue;
115 } else { 218 } else {
116 // 7. Collect a sequence of characters that are not "," (U+002C) cha racters, and let that be descriptors. 219 // Advancing position here (contrary to spec) to avoid an useless ex tra state machine step.
117 skipWhile<CharType, isHTMLSpace<CharType> >(position, attributeEnd); 220 // Filed a spec bug: https://github.com/ResponsiveImagesCG/picture-e lement/issues/189
118 const CharType* descriptorsStart = position; 221 ++position;
119 skipUntil<CharType, isComma<CharType> >(position, attributeEnd); 222 Vector<DescriptorToken> descriptorTokens;
120 const CharType* descriptorsEnd = position; 223 tokenizeDescriptors(attributeStart, position, attributeEnd, descript orTokens);
121 if (!parseDescriptors(descriptorsStart, descriptorsEnd, result)) 224 // Contrary to spec language - descriptor parsing happens on each ca ndidate.
225 // This is a black-box equivalent, to avoid storing descriptor lists for each candidate.
226 if (!parseDescriptors(attribute, descriptorTokens, result))
122 continue; 227 continue;
123 } 228 }
124 229
125 ASSERT(imageURLEnd > attributeStart); 230 ASSERT(imageURLEnd > attributeStart);
126 unsigned imageURLStartingPosition = imageURLStart - attributeStart; 231 unsigned imageURLStartingPosition = imageURLStart - attributeStart;
127 ASSERT(imageURLEnd > imageURLStart); 232 ASSERT(imageURLEnd > imageURLStart);
128 unsigned imageURLLength = imageURLEnd - imageURLStart; 233 unsigned imageURLLength = imageURLEnd - imageURLStart;
129 imageCandidates.append(ImageCandidate(attribute, imageURLStartingPositio n, imageURLLength, result, ImageCandidate::SrcsetOrigin)); 234 imageCandidates.append(ImageCandidate(attribute, imageURLStartingPositio n, imageURLLength, result, ImageCandidate::SrcsetOrigin));
130 // 11. Return to the step labeled splitting loop. 235 // 11. Return to the step labeled splitting loop.
131 } 236 }
132 } 237 }
133 238
134 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, Vec tor<ImageCandidate>& imageCandidates) 239 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, Vec tor<ImageCandidate>& imageCandidates)
135 { 240 {
136 if (attribute.isNull()) 241 if (attribute.isNull())
137 return; 242 return;
138 243
139 if (attribute.is8Bit()) 244 if (attribute.is8Bit())
140 parseImageCandidatesFromSrcsetAttribute<LChar>(attribute, attribute.char acters8(), attribute.length(), imageCandidates); 245 parseImageCandidatesFromSrcsetAttribute<LChar>(attribute, attribute.char acters8(), attribute.length(), imageCandidates);
141 else 246 else
142 parseImageCandidatesFromSrcsetAttribute<UChar>(attribute, attribute.char acters16(), attribute.length(), imageCandidates); 247 parseImageCandidatesFromSrcsetAttribute<UChar>(attribute, attribute.char acters16(), attribute.length(), imageCandidates);
143 } 248 }
144 249
145 static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, unsigned s ourceSize, Vector<ImageCandidate>& imageCandidates) 250 static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, unsigned s ourceSize, Vector<ImageCandidate>& imageCandidates)
146 { 251 {
252 const float defaultDensityValue = 1.0;
147 bool ignoreSrc = false; 253 bool ignoreSrc = false;
148 if (imageCandidates.isEmpty()) 254 if (imageCandidates.isEmpty())
149 return ImageCandidate(); 255 return ImageCandidate();
150 256
151 // http://picture.responsiveimages.org/#normalize-source-densities 257 // http://picture.responsiveimages.org/#normalize-source-densities
152 for (Vector<ImageCandidate>::iterator it = imageCandidates.begin(); it != im ageCandidates.end(); ++it) { 258 for (Vector<ImageCandidate>::iterator it = imageCandidates.begin(); it != im ageCandidates.end(); ++it) {
153 if (it->resourceWidth() > 0) { 259 if (it->resourceWidth() > 0) {
154 it->setScaleFactor((float)it->resourceWidth() / (float)sourceSize); 260 it->setDensity((float)it->resourceWidth() / (float)sourceSize);
155 ignoreSrc = true; 261 ignoreSrc = true;
262 } else if (it->density() < 0) {
263 it->setDensity(defaultDensityValue);
156 } 264 }
157 } 265 }
158 266
159 std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareBySc aleFactor); 267 std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByDe nsity);
160 268
161 unsigned i; 269 unsigned i;
162 for (i = 0; i < imageCandidates.size() - 1; ++i) { 270 for (i = 0; i < imageCandidates.size() - 1; ++i) {
163 if ((imageCandidates[i].scaleFactor() >= deviceScaleFactor) && (!ignoreS rc || !imageCandidates[i].srcOrigin())) 271 if ((imageCandidates[i].density() >= deviceScaleFactor) && (!ignoreSrc | | !imageCandidates[i].srcOrigin()))
164 break; 272 break;
165 } 273 }
166 274
167 if (imageCandidates[i].srcOrigin() && ignoreSrc) { 275 if (imageCandidates[i].srcOrigin() && ignoreSrc) {
168 ASSERT(i > 0); 276 ASSERT(i > 0);
169 --i; 277 --i;
170 } 278 }
171 float winningScaleFactor = imageCandidates[i].scaleFactor(); 279 float winningDensity = imageCandidates[i].density();
172 280
173 unsigned winner = i; 281 unsigned winner = i;
174 // 16. If an entry b in candidates has the same associated ... pixel density as an earlier entry a in candidates, 282 // 16. If an entry b in candidates has the same associated ... pixel density as an earlier entry a in candidates,
175 // then remove entry b 283 // then remove entry b
176 while ((i > 0) && (imageCandidates[--i].scaleFactor() == winningScaleFactor) ) 284 while ((i > 0) && (imageCandidates[--i].density() == winningDensity))
177 winner = i; 285 winner = i;
178 286
179 return imageCandidates[winner]; 287 return imageCandidates[winner];
180 } 288 }
181 289
182 ImageCandidate bestFitSourceForSrcsetAttribute(float deviceScaleFactor, unsigned sourceSize, const String& srcsetAttribute) 290 ImageCandidate bestFitSourceForSrcsetAttribute(float deviceScaleFactor, unsigned sourceSize, const String& srcsetAttribute)
183 { 291 {
184 Vector<ImageCandidate> imageCandidates; 292 Vector<ImageCandidate> imageCandidates;
185 293
186 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates); 294 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates);
187 295
188 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ); 296 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates );
189 } 297 }
190 298
191 ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceSize, const String& srcAttribute, const String& srcsetAttribute) 299 ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceSize, const String& srcAttribute, const String& srcsetAttribute)
192 { 300 {
193 DescriptorParsingResult defaultResult;
194 defaultResult.scaleFactor = 1.0;
195
196 if (srcsetAttribute.isNull()) { 301 if (srcsetAttribute.isNull()) {
197 if (srcAttribute.isNull()) 302 if (srcAttribute.isNull())
198 return ImageCandidate(); 303 return ImageCandidate();
199 return ImageCandidate(srcAttribute, 0, srcAttribute.length(), defaultRes ult, ImageCandidate::SrcOrigin); 304 return ImageCandidate(srcAttribute, 0, srcAttribute.length(), Descriptor ParsingResult(), ImageCandidate::SrcOrigin);
200 } 305 }
201 306
202 Vector<ImageCandidate> imageCandidates; 307 Vector<ImageCandidate> imageCandidates;
203 308
204 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates); 309 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates);
205 310
206 if (!srcAttribute.isEmpty()) 311 if (!srcAttribute.isEmpty())
207 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), defaultResult, ImageCandidate::SrcOrigin)); 312 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), DescriptorParsingResult(), ImageCandidate::SrcOrigin));
208 313
209 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ); 314 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates );
210 } 315 }
211 316
212 String bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceS ize, const String& srcAttribute, ImageCandidate& srcsetImageCandidate) 317 String bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceS ize, const String& srcAttribute, ImageCandidate& srcsetImageCandidate)
213 { 318 {
214 DescriptorParsingResult defaultResult;
215 defaultResult.scaleFactor = 1.0;
216
217 if (srcsetImageCandidate.isEmpty()) 319 if (srcsetImageCandidate.isEmpty())
218 return srcAttribute; 320 return srcAttribute;
219 321
220 Vector<ImageCandidate> imageCandidates; 322 Vector<ImageCandidate> imageCandidates;
221 imageCandidates.append(srcsetImageCandidate); 323 imageCandidates.append(srcsetImageCandidate);
222 324
223 if (!srcAttribute.isEmpty()) 325 if (!srcAttribute.isEmpty())
224 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), defaultResult, ImageCandidate::SrcOrigin)); 326 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), DescriptorParsingResult(), ImageCandidate::SrcOrigin));
225 327
226 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ).toString(); 328 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ).toString();
227 } 329 }
228 330
229 } 331 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698