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

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

Powered by Google App Engine
This is Rietveld 408576698