ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, const AtomicString& srcAttribute, const AtomicString& srcsetAttribute #if ENABLE(PICTURE_SIZES) , unsigned sourceSize #endif ) { if (srcsetAttribute.isNull()) { if (srcAttribute.isNull()) return ImageCandidate(); return ImageCandidate(StringView(srcAttribute), DescriptorParsingResult(), ImageCandidate::SrcOrigin); } Vector<ImageCandidate> imageCandidates; parseImageCandidatesFromSrcsetAttribute(StringView(srcsetAttribute), imageCandidates); if (!srcAttribute.isEmpty()) imageCandidates.append(ImageCandidate(StringView(srcAttribute), DescriptorParsingResult(), ImageCandidate::SrcOrigin)); return pickBestImageCandidate(deviceScaleFactor, imageCandidates #if ENABLE(PICTURE_SIZES) , sourceSize #endif ); }
ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceSize, const String& srcAttribute, const String& srcsetAttribute) { if (srcsetAttribute.isNull()) { if (srcAttribute.isNull()) return ImageCandidate(); return ImageCandidate(srcAttribute, 0, srcAttribute.length(), DescriptorParsingResult(), ImageCandidate::SrcOrigin); } Vector<ImageCandidate> imageCandidates; parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates); if (!srcAttribute.isEmpty()) imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.length(), DescriptorParsingResult(), ImageCandidate::SrcOrigin)); return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates); }
static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, const CharType* attributeStart, unsigned length, Vector<ImageCandidate>& imageCandidates, Document* document) { const CharType* position = attributeStart; const CharType* attributeEnd = position + length; while (position < attributeEnd) { // 4. Splitting loop: Collect a sequence of characters that are space characters or U+002C COMMA characters. skipWhile<CharType, isHTMLSpaceOrComma<CharType>>(position, attributeEnd); if (position == attributeEnd) { // Contrary to spec language - descriptor parsing happens on each candidate, so when we reach the attributeEnd, we can exit. break; } const CharType* imageURLStart = position; // 6. Collect a sequence of characters that are not space characters, and let that be url. skipUntil<CharType, isHTMLSpace<CharType>>(position, attributeEnd); const CharType* imageURLEnd = position; DescriptorParsingResult result; // 8. If url ends with a U+002C COMMA character (,) if (isComma(*(position - 1))) { // Remove all trailing U+002C COMMA characters from url. imageURLEnd = position - 1; reverseSkipWhile<CharType, isComma>(imageURLEnd, imageURLStart); ++imageURLEnd; // If url is empty, then jump to the step labeled splitting loop. if (imageURLStart == imageURLEnd) continue; } else { skipWhile<CharType, isHTMLSpace<CharType>>(position, attributeEnd); Vector<DescriptorToken> descriptorTokens; tokenizeDescriptors(attributeStart, position, attributeEnd, descriptorTokens); // Contrary to spec language - descriptor parsing happens on each candidate. // This is a black-box equivalent, to avoid storing descriptor lists for each candidate. if (!parseDescriptors(attribute, descriptorTokens, result, document)) { if (document) { UseCounter::count(document, UseCounter::SrcsetDroppedCandidate); if (document->frame()) document->frame()->console().addMessage(ConsoleMessage::create(OtherMessageSource, ErrorMessageLevel, String("Dropped srcset candidate ") + String(imageURLStart, imageURLEnd - imageURLStart))); } continue; } } ASSERT(imageURLEnd > attributeStart); unsigned imageURLStartingPosition = imageURLStart - attributeStart; ASSERT(imageURLEnd > imageURLStart); unsigned imageURLLength = imageURLEnd - imageURLStart; imageCandidates.append(ImageCandidate(attribute, imageURLStartingPosition, imageURLLength, result, ImageCandidate::SrcsetOrigin)); // 11. Return to the step labeled splitting loop. } }
String bestFitSourceForImageAttributes(float deviceScaleFactor, float sourceSize, const String& srcAttribute, ImageCandidate& srcsetImageCandidate) { if (srcsetImageCandidate.isEmpty()) return srcAttribute; Vector<ImageCandidate> imageCandidates; imageCandidates.append(srcsetImageCandidate); if (!srcAttribute.isEmpty()) imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.length(), DescriptorParsingResult(), ImageCandidate::SrcOrigin)); return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates).toString(); }
static Vector<ImageCandidate> parseImageCandidatesFromSrcsetAttribute(const CharType* attributeStart, unsigned length) { Vector<ImageCandidate> imageCandidates; const CharType* attributeEnd = attributeStart + length; for (const CharType* position = attributeStart; position < attributeEnd;) { // 4. Splitting loop: Collect a sequence of characters that are space characters or U+002C COMMA characters. skipWhile<CharType, isHTMLSpaceOrComma<CharType> >(position, attributeEnd); if (position == attributeEnd) { // Contrary to spec language - descriptor parsing happens on each candidate, so when we reach the attributeEnd, we can exit. break; } const CharType* imageURLStart = position; // 6. Collect a sequence of characters that are not space characters, and let that be url. skipUntil<CharType, isHTMLSpace<CharType> >(position, attributeEnd); const CharType* imageURLEnd = position; DescriptorParsingResult result; // 8. If url ends with a U+002C COMMA character (,) if (isComma(*(position - 1))) { // Remove all trailing U+002C COMMA characters from url. imageURLEnd = position - 1; reverseSkipWhile<CharType, isComma>(imageURLEnd, imageURLStart); ++imageURLEnd; // If url is empty, then jump to the step labeled splitting loop. if (imageURLStart == imageURLEnd) continue; } else { // Advancing position here (contrary to spec) to avoid an useless extra state machine step. // Filed a spec bug: https://github.com/ResponsiveImagesCG/picture-element/issues/189 ++position; Vector<StringView> descriptorTokens; tokenizeDescriptors(position, attributeEnd, descriptorTokens); // Contrary to spec language - descriptor parsing happens on each candidate. // This is a black-box equivalent, to avoid storing descriptor lists for each candidate. if (!parseDescriptors(descriptorTokens, result)) continue; } ASSERT(imageURLEnd > imageURLStart); unsigned imageURLLength = imageURLEnd - imageURLStart; imageCandidates.append(ImageCandidate(StringView(imageURLStart, imageURLLength), result, ImageCandidate::SrcsetOrigin)); // 11. Return to the step labeled splitting loop. } return imageCandidates; }
static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, Vector<ImageCandidate>& imageCandidates #if ENABLE(PICTURE_SIZES) , unsigned sourceSize #endif ) { bool ignoreSrc = false; if (imageCandidates.isEmpty()) return ImageCandidate(); // http://picture.responsiveimages.org/#normalize-source-densities for (auto& candidate : imageCandidates) { #if ENABLE(PICTURE_SIZES) if (candidate.resourceWidth > 0) { candidate.density = static_cast<float>(candidate.resourceWidth) / static_cast<float>(sourceSize); ignoreSrc = true; } else #endif if (candidate.density < 0) candidate.density = DefaultDensityValue; } std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByDensity); unsigned i; for (i = 0; i < imageCandidates.size() - 1; ++i) { if ((imageCandidates[i].density >= deviceScaleFactor) && (!ignoreSrc || !imageCandidates[i].srcOrigin())) break; } if (imageCandidates[i].srcOrigin() && ignoreSrc) { ASSERT(i > 0); --i; } float winningDensity = imageCandidates[i].density; unsigned winner = i; // 16. If an entry b in candidates has the same associated ... pixel density as an earlier entry a in candidates, // then remove entry b while ((i > 0) && (imageCandidates[--i].density == winningDensity)) winner = i; return imageCandidates[winner]; }
static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, unsigned sourceSize, Vector<ImageCandidate>& imageCandidates) { const float defaultDensityValue = 1.0; bool ignoreSrc = false; if (imageCandidates.isEmpty()) return ImageCandidate(); // http://picture.responsiveimages.org/#normalize-source-densities for (Vector<ImageCandidate>::iterator it = imageCandidates.begin(); it != imageCandidates.end(); ++it) { if (it->resourceWidth() > 0) { it->setDensity((float)it->resourceWidth() / (float)sourceSize); ignoreSrc = true; } else if (it->density() < 0) { it->setDensity(defaultDensityValue); } } std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByDensity); unsigned i; for (i = 0; i < imageCandidates.size() - 1; ++i) { if ((imageCandidates[i].density() >= deviceScaleFactor) && (!ignoreSrc || !imageCandidates[i].srcOrigin())) break; } if (imageCandidates[i].srcOrigin() && ignoreSrc) { ASSERT(i > 0); --i; } float winningDensity = imageCandidates[i].density(); unsigned winner = i; // 16. If an entry b in candidates has the same associated ... pixel density as an earlier entry a in candidates, // then remove entry b while ((i > 0) && (imageCandidates[--i].density() == winningDensity)) winner = i; return imageCandidates[winner]; }
static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, float sourceSize, Vector<ImageCandidate>& imageCandidates, Document* document = nullptr) { const float defaultDensityValue = 1.0; bool ignoreSrc = false; if (imageCandidates.isEmpty()) return ImageCandidate(); // http://picture.responsiveimages.org/#normalize-source-densities for (ImageCandidate& image : imageCandidates) { if (image.resourceWidth() > 0) { image.setDensity((float)image.resourceWidth() / sourceSize); ignoreSrc = true; } else if (image.density() < 0) { image.setDensity(defaultDensityValue); } } std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByDensity); Vector<ImageCandidate*> deDupedImageCandidates; float prevDensity = -1.0; for (ImageCandidate& image : imageCandidates) { if (image.density() != prevDensity && (!ignoreSrc || !image.srcOrigin())) deDupedImageCandidates.append(&image); prevDensity = image.density(); } unsigned winner = selectionLogic(deDupedImageCandidates, deviceScaleFactor); ASSERT(winner < deDupedImageCandidates.size()); winner = avoidDownloadIfHigherDensityResourceIsInCache(deDupedImageCandidates, winner, document); float winningDensity = deDupedImageCandidates[winner]->density(); // 16. If an entry b in candidates has the same associated ... pixel density as an earlier entry a in candidates, // then remove entry b while ((winner > 0) && (deDupedImageCandidates[winner - 1]->density() == winningDensity)) --winner; return *deDupedImageCandidates[winner]; }