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

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

Issue 649183007: Add console errors and usecounter when srcset candidates are dropped (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: rebase Created 6 years, 2 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 14 matching lines...) Expand all
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */ 30 */
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 "core/dom/Document.h"
36 #include "core/frame/FrameConsole.h"
37 #include "core/frame/LocalFrame.h"
38 #include "core/frame/UseCounter.h"
35 #include "core/html/parser/HTMLParserIdioms.h" 39 #include "core/html/parser/HTMLParserIdioms.h"
40 #include "core/inspector/ConsoleMessage.h"
36 #include "platform/ParsingUtilities.h" 41 #include "platform/ParsingUtilities.h"
37 #include "platform/RuntimeEnabledFeatures.h" 42 #include "platform/RuntimeEnabledFeatures.h"
38 43
39 namespace blink { 44 namespace blink {
40 45
41 static bool compareByDensity(const ImageCandidate& first, const ImageCandidate& second) 46 static bool compareByDensity(const ImageCandidate& first, const ImageCandidate& second)
42 { 47 {
43 return first.density() < second.density(); 48 return first.density() < second.density();
44 } 49 }
45 50
46 enum DescriptorTokenizerState { 51 enum DescriptorTokenizerState {
47 Start, 52 TokenStart,
48 InParenthesis, 53 InParenthesis,
49 AfterToken, 54 AfterToken,
50 }; 55 };
51 56
52 struct DescriptorToken { 57 struct DescriptorToken {
53 unsigned start; 58 unsigned start;
54 unsigned length; 59 unsigned length;
55 60
56 DescriptorToken(unsigned start, unsigned length) 61 DescriptorToken(unsigned start, unsigned length)
57 : start(start) 62 : start(start)
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 { 122 {
118 return position >= end; 123 return position >= end;
119 } 124 }
120 125
121 template<typename CharType> 126 template<typename CharType>
122 static void tokenizeDescriptors(const CharType* attributeStart, 127 static void tokenizeDescriptors(const CharType* attributeStart,
123 const CharType*& position, 128 const CharType*& position,
124 const CharType* attributeEnd, 129 const CharType* attributeEnd,
125 Vector<DescriptorToken>& descriptors) 130 Vector<DescriptorToken>& descriptors)
126 { 131 {
127 DescriptorTokenizerState state = Start; 132 DescriptorTokenizerState state = TokenStart;
128 const CharType* descriptorsStart = position; 133 const CharType* descriptorsStart = position;
129 const CharType* currentDescriptorStart = descriptorsStart; 134 const CharType* currentDescriptorStart = descriptorsStart;
130 while (true) { 135 while (true) {
131 switch (state) { 136 switch (state) {
132 case Start: 137 case TokenStart:
133 if (isEOF(position, attributeEnd)) { 138 if (isEOF(position, attributeEnd)) {
134 appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors); 139 appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors);
135 return; 140 return;
136 } 141 }
137 if (isComma(*position)) { 142 if (isComma(*position)) {
138 appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors); 143 appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors);
139 ++position; 144 ++position;
140 return; 145 return;
141 } 146 }
142 if (isHTMLSpace(*position)) { 147 if (isHTMLSpace(*position)) {
143 appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors); 148 appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors);
144 currentDescriptorStart = position + 1; 149 currentDescriptorStart = position + 1;
145 state = AfterToken; 150 state = AfterToken;
146 } else if (*position == '(') { 151 } else if (*position == '(') {
147 appendCharacter(currentDescriptorStart, position); 152 appendCharacter(currentDescriptorStart, position);
148 state = InParenthesis; 153 state = InParenthesis;
149 } else { 154 } else {
150 appendCharacter(currentDescriptorStart, position); 155 appendCharacter(currentDescriptorStart, position);
151 } 156 }
152 break; 157 break;
153 case InParenthesis: 158 case InParenthesis:
154 if (isEOF(position, attributeEnd)) { 159 if (isEOF(position, attributeEnd)) {
155 appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors); 160 appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors);
156 return; 161 return;
157 } 162 }
158 if (*position == ')') { 163 if (*position == ')') {
159 appendCharacter(currentDescriptorStart, position); 164 appendCharacter(currentDescriptorStart, position);
160 state = Start; 165 state = TokenStart;
161 } else { 166 } else {
162 appendCharacter(currentDescriptorStart, position); 167 appendCharacter(currentDescriptorStart, position);
163 } 168 }
164 break; 169 break;
165 case AfterToken: 170 case AfterToken:
166 if (isEOF(position, attributeEnd)) 171 if (isEOF(position, attributeEnd))
167 return; 172 return;
168 if (!isHTMLSpace(*position)) { 173 if (!isHTMLSpace(*position)) {
169 state = Start; 174 state = TokenStart;
170 currentDescriptorStart = position; 175 currentDescriptorStart = position;
171 --position; 176 --position;
172 } 177 }
173 break; 178 break;
174 } 179 }
175 ++position; 180 ++position;
176 } 181 }
177 } 182 }
178 183
184 static void consoleMessage(Document* document, String message)
185 {
186 if (document && document->frame())
187 document->frame()->console().addMessage(ConsoleMessage::create(OtherMess ageSource, ErrorMessageLevel, message));
188 }
189
179 template<typename CharType> 190 template<typename CharType>
180 static bool parseDescriptors(const CharType* attribute, Vector<DescriptorToken>& descriptors, DescriptorParsingResult& result) 191 static bool parseDescriptors(const CharType* attribute, Vector<DescriptorToken>& descriptors, DescriptorParsingResult& result, Document* document)
181 { 192 {
182 for (Vector<DescriptorToken>::iterator it = descriptors.begin(); it != descr iptors.end(); ++it) { 193 for (Vector<DescriptorToken>::iterator it = descriptors.begin(); it != descr iptors.end(); ++it) {
183 if (it->length == 0) 194 if (it->length == 0)
184 continue; 195 continue;
185 CharType c = attribute[it->lastIndex()]; 196 CharType c = attribute[it->lastIndex()];
186 bool isValid = false; 197 bool isValid = false;
187 if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'w') { 198 if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'w') {
188 if (result.hasDensity() || result.hasWidth()) 199 if (result.hasDensity() || result.hasWidth()) {
200 consoleMessage(document, "srcset has multiple 'w' descriptors or a mix of 'x' and 'w' descriptors.");
189 return false; 201 return false;
202 }
190 int resourceWidth = it->toInt(attribute, isValid); 203 int resourceWidth = it->toInt(attribute, isValid);
191 if (!isValid || resourceWidth <= 0) 204 if (!isValid || resourceWidth <= 0) {
205 consoleMessage(document, "srcset 'w' descriptor is invalid.");
192 return false; 206 return false;
207 }
193 result.setResourceWidth(resourceWidth); 208 result.setResourceWidth(resourceWidth);
194 } else if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'h') { 209 } else if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'h') {
195 // This is here only for future compat purposes. 210 // This is here only for future compat purposes.
196 // The value of the 'h' descriptor is not used. 211 // The value of the 'h' descriptor is not used.
197 if (result.hasDensity() || result.hasHeight()) 212 if (result.hasDensity() || result.hasHeight()) {
213 consoleMessage(document, "srcset has multiple 'h' descriptors or a mix of 'x' and 'h' descriptors.");
198 return false; 214 return false;
215 }
199 int resourceHeight = it->toInt(attribute, isValid); 216 int resourceHeight = it->toInt(attribute, isValid);
200 if (!isValid || resourceHeight <= 0) 217 if (!isValid || resourceHeight <= 0) {
218 consoleMessage(document, "srcset 'h' descriptor is invalid.");
201 return false; 219 return false;
220 }
202 result.setResourceHeight(resourceHeight); 221 result.setResourceHeight(resourceHeight);
203 } else if (c == 'x') { 222 } else if (c == 'x') {
204 if (result.hasDensity() || result.hasHeight() || result.hasWidth()) 223 if (result.hasDensity() || result.hasHeight() || result.hasWidth()) {
224 consoleMessage(document, "srcset has multiple 'x' descriptors or a mix of 'x' and 'w'/'h' descriptors.");
205 return false; 225 return false;
226 }
206 float density = it->toFloat(attribute, isValid); 227 float density = it->toFloat(attribute, isValid);
207 if (!isValid || density < 0) 228 if (!isValid || density < 0) {
229 consoleMessage(document, "srcset 'x' descriptor is invalid.");
208 return false; 230 return false;
231 }
209 result.setDensity(density); 232 result.setDensity(density);
210 } else { 233 } else {
234 consoleMessage(document, "srcset has an unknown descriptor.");
211 return false; 235 return false;
212 } 236 }
213 } 237 }
214 return (!result.hasHeight() || result.hasWidth()); 238 bool res = !result.hasHeight() || result.hasWidth();
239 if (!res)
240 consoleMessage(document, "srcset has an 'h' descriptor and no 'w' descri ptor.");
241 return res;
215 } 242 }
216 243
217 static bool parseDescriptors(const String& attribute, Vector<DescriptorToken>& d escriptors, DescriptorParsingResult& result) 244 static bool parseDescriptors(const String& attribute, Vector<DescriptorToken>& d escriptors, DescriptorParsingResult& result, Document* document)
218 { 245 {
219 // FIXME: See if StringView can't be extended to replace DescriptorToken her e. 246 // FIXME: See if StringView can't be extended to replace DescriptorToken her e.
220 if (attribute.is8Bit()) { 247 if (attribute.is8Bit()) {
221 return parseDescriptors(attribute.characters8(), descriptors, result); 248 return parseDescriptors(attribute.characters8(), descriptors, result, do cument);
222 } 249 }
223 return parseDescriptors(attribute.characters16(), descriptors, result); 250 return parseDescriptors(attribute.characters16(), descriptors, result, docum ent);
224 } 251 }
225 252
226 // http://picture.responsiveimages.org/#parse-srcset-attr 253 // http://picture.responsiveimages.org/#parse-srcset-attr
227 template<typename CharType> 254 template<typename CharType>
228 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, con st CharType* attributeStart, unsigned length, Vector<ImageCandidate>& imageCandi dates) 255 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, con st CharType* attributeStart, unsigned length, Vector<ImageCandidate>& imageCandi dates, Document* document)
229 { 256 {
230 const CharType* position = attributeStart; 257 const CharType* position = attributeStart;
231 const CharType* attributeEnd = position + length; 258 const CharType* attributeEnd = position + length;
232 259
233 while (position < attributeEnd) { 260 while (position < attributeEnd) {
234 // 4. Splitting loop: Collect a sequence of characters that are space ch aracters or U+002C COMMA characters. 261 // 4. Splitting loop: Collect a sequence of characters that are space ch aracters or U+002C COMMA characters.
235 skipWhile<CharType, isHTMLSpaceOrComma<CharType> >(position, attributeEn d); 262 skipWhile<CharType, isHTMLSpaceOrComma<CharType> >(position, attributeEn d);
236 if (position == attributeEnd) { 263 if (position == attributeEnd) {
237 // Contrary to spec language - descriptor parsing happens on each ca ndidate, so when we reach the attributeEnd, we can exit. 264 // Contrary to spec language - descriptor parsing happens on each ca ndidate, so when we reach the attributeEnd, we can exit.
238 break; 265 break;
239 } 266 }
240 const CharType* imageURLStart = position; 267 const CharType* imageURLStart = position;
268
241 // 6. Collect a sequence of characters that are not space characters, an d let that be url. 269 // 6. Collect a sequence of characters that are not space characters, an d let that be url.
242
243 skipUntil<CharType, isHTMLSpace<CharType> >(position, attributeEnd); 270 skipUntil<CharType, isHTMLSpace<CharType> >(position, attributeEnd);
244 const CharType* imageURLEnd = position; 271 const CharType* imageURLEnd = position;
245 272
246 DescriptorParsingResult result; 273 DescriptorParsingResult result;
247 274
248 // 8. If url ends with a U+002C COMMA character (,) 275 // 8. If url ends with a U+002C COMMA character (,)
249 if (isComma(*(position - 1))) { 276 if (isComma(*(position - 1))) {
250 // Remove all trailing U+002C COMMA characters from url. 277 // Remove all trailing U+002C COMMA characters from url.
251 imageURLEnd = position - 1; 278 imageURLEnd = position - 1;
252 reverseSkipWhile<CharType, isComma>(imageURLEnd, imageURLStart); 279 reverseSkipWhile<CharType, isComma>(imageURLEnd, imageURLStart);
253 ++imageURLEnd; 280 ++imageURLEnd;
254 // If url is empty, then jump to the step labeled splitting loop. 281 // If url is empty, then jump to the step labeled splitting loop.
255 if (imageURLStart == imageURLEnd) 282 if (imageURLStart == imageURLEnd)
256 continue; 283 continue;
257 } else { 284 } else {
258 // Advancing position here (contrary to spec) to avoid an useless ex tra state machine step. 285 skipWhile<CharType, isHTMLSpace<CharType> >(position, attributeEnd);
259 // Filed a spec bug: https://github.com/ResponsiveImagesCG/picture-e lement/issues/189
260 ++position;
261 Vector<DescriptorToken> descriptorTokens; 286 Vector<DescriptorToken> descriptorTokens;
262 tokenizeDescriptors(attributeStart, position, attributeEnd, descript orTokens); 287 tokenizeDescriptors(attributeStart, position, attributeEnd, descript orTokens);
263 // Contrary to spec language - descriptor parsing happens on each ca ndidate. 288 // Contrary to spec language - descriptor parsing happens on each ca ndidate.
264 // This is a black-box equivalent, to avoid storing descriptor lists for each candidate. 289 // This is a black-box equivalent, to avoid storing descriptor lists for each candidate.
265 if (!parseDescriptors(attribute, descriptorTokens, result)) 290 if (!parseDescriptors(attribute, descriptorTokens, result, document) ) {
291 if (document) {
292 UseCounter::count(document, UseCounter::SrcsetDroppedCandida te);
293 if (document->frame())
294 document->frame()->console().addMessage(ConsoleMessage:: create(OtherMessageSource, ErrorMessageLevel, String("Dropped srcset candidate " ) + String(imageURLStart, imageURLEnd - imageURLStart)));
295 }
266 continue; 296 continue;
297 }
267 } 298 }
268 299
269 ASSERT(imageURLEnd > attributeStart); 300 ASSERT(imageURLEnd > attributeStart);
270 unsigned imageURLStartingPosition = imageURLStart - attributeStart; 301 unsigned imageURLStartingPosition = imageURLStart - attributeStart;
271 ASSERT(imageURLEnd > imageURLStart); 302 ASSERT(imageURLEnd > imageURLStart);
272 unsigned imageURLLength = imageURLEnd - imageURLStart; 303 unsigned imageURLLength = imageURLEnd - imageURLStart;
273 imageCandidates.append(ImageCandidate(attribute, imageURLStartingPositio n, imageURLLength, result, ImageCandidate::SrcsetOrigin)); 304 imageCandidates.append(ImageCandidate(attribute, imageURLStartingPositio n, imageURLLength, result, ImageCandidate::SrcsetOrigin));
274 // 11. Return to the step labeled splitting loop. 305 // 11. Return to the step labeled splitting loop.
275 } 306 }
276 } 307 }
277 308
278 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, Vec tor<ImageCandidate>& imageCandidates) 309 static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, Vec tor<ImageCandidate>& imageCandidates, Document* document)
279 { 310 {
280 if (attribute.isNull()) 311 if (attribute.isNull())
281 return; 312 return;
282 313
283 if (attribute.is8Bit()) 314 if (attribute.is8Bit())
284 parseImageCandidatesFromSrcsetAttribute<LChar>(attribute, attribute.char acters8(), attribute.length(), imageCandidates); 315 parseImageCandidatesFromSrcsetAttribute<LChar>(attribute, attribute.char acters8(), attribute.length(), imageCandidates, document);
285 else 316 else
286 parseImageCandidatesFromSrcsetAttribute<UChar>(attribute, attribute.char acters16(), attribute.length(), imageCandidates); 317 parseImageCandidatesFromSrcsetAttribute<UChar>(attribute, attribute.char acters16(), attribute.length(), imageCandidates, document);
287 } 318 }
288 319
289 static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, float sour ceSize, Vector<ImageCandidate>& imageCandidates) 320 static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, float sour ceSize, Vector<ImageCandidate>& imageCandidates)
290 { 321 {
291 const float defaultDensityValue = 1.0; 322 const float defaultDensityValue = 1.0;
292 bool ignoreSrc = false; 323 bool ignoreSrc = false;
293 if (imageCandidates.isEmpty()) 324 if (imageCandidates.isEmpty())
294 return ImageCandidate(); 325 return ImageCandidate();
295 326
296 // http://picture.responsiveimages.org/#normalize-source-densities 327 // http://picture.responsiveimages.org/#normalize-source-densities
(...skipping 22 matching lines...) Expand all
319 350
320 unsigned winner = i; 351 unsigned winner = i;
321 // 16. If an entry b in candidates has the same associated ... pixel density as an earlier entry a in candidates, 352 // 16. If an entry b in candidates has the same associated ... pixel density as an earlier entry a in candidates,
322 // then remove entry b 353 // then remove entry b
323 while ((i > 0) && (imageCandidates[--i].density() == winningDensity)) 354 while ((i > 0) && (imageCandidates[--i].density() == winningDensity))
324 winner = i; 355 winner = i;
325 356
326 return imageCandidates[winner]; 357 return imageCandidates[winner];
327 } 358 }
328 359
329 ImageCandidate bestFitSourceForSrcsetAttribute(float deviceScaleFactor, float so urceSize, const String& srcsetAttribute) 360 ImageCandidate bestFitSourceForSrcsetAttribute(float deviceScaleFactor, float so urceSize, const String& srcsetAttribute, Document* document)
330 { 361 {
331 Vector<ImageCandidate> imageCandidates; 362 Vector<ImageCandidate> imageCandidates;
332 363
333 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates); 364 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates, do cument);
334 365
335 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ); 366 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates );
336 } 367 }
337 368
338 ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, float so urceSize, const String& srcAttribute, const String& srcsetAttribute) 369 ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, float so urceSize, const String& srcAttribute, const String& srcsetAttribute, Document* d ocument)
339 { 370 {
340 if (srcsetAttribute.isNull()) { 371 if (srcsetAttribute.isNull()) {
341 if (srcAttribute.isNull()) 372 if (srcAttribute.isNull())
342 return ImageCandidate(); 373 return ImageCandidate();
343 return ImageCandidate(srcAttribute, 0, srcAttribute.length(), Descriptor ParsingResult(), ImageCandidate::SrcOrigin); 374 return ImageCandidate(srcAttribute, 0, srcAttribute.length(), Descriptor ParsingResult(), ImageCandidate::SrcOrigin);
344 } 375 }
345 376
346 Vector<ImageCandidate> imageCandidates; 377 Vector<ImageCandidate> imageCandidates;
347 378
348 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates); 379 parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates, do cument);
349 380
350 if (!srcAttribute.isEmpty()) 381 if (!srcAttribute.isEmpty())
351 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), DescriptorParsingResult(), ImageCandidate::SrcOrigin)); 382 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), DescriptorParsingResult(), ImageCandidate::SrcOrigin));
352 383
353 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ); 384 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates );
354 } 385 }
355 386
356 String bestFitSourceForImageAttributes(float deviceScaleFactor, float sourceSize , const String& srcAttribute, ImageCandidate& srcsetImageCandidate) 387 String bestFitSourceForImageAttributes(float deviceScaleFactor, float sourceSize , const String& srcAttribute, ImageCandidate& srcsetImageCandidate)
357 { 388 {
358 if (srcsetImageCandidate.isEmpty()) 389 if (srcsetImageCandidate.isEmpty())
359 return srcAttribute; 390 return srcAttribute;
360 391
361 Vector<ImageCandidate> imageCandidates; 392 Vector<ImageCandidate> imageCandidates;
362 imageCandidates.append(srcsetImageCandidate); 393 imageCandidates.append(srcsetImageCandidate);
363 394
364 if (!srcAttribute.isEmpty()) 395 if (!srcAttribute.isEmpty())
365 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), DescriptorParsingResult(), ImageCandidate::SrcOrigin)); 396 imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.leng th(), DescriptorParsingResult(), ImageCandidate::SrcOrigin));
366 397
367 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ).toString(); 398 return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates ).toString();
368 } 399 }
369 400
370 } 401 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698