String bestFitSourceForImageAttributes(float deviceScaleFactor, const String& srcAttribute, const String& srcsetAttribute) { ImageCandidates imageCandidates; parseImagesWithScaleFromSrcsetAttribute(srcsetAttribute, imageCandidates); if (!srcAttribute.isEmpty()) { ImageWithScale image; image.imageURL = srcAttribute; image.scaleFactor = 1.0; imageCandidates.append(image); } if (imageCandidates.isEmpty()) return String(); std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByScaleFactor); for (size_t i = 0; i < imageCandidates.size() - 1; ++i) { if (imageCandidates[i].scaleFactor >= deviceScaleFactor) return String(imageCandidates[i].imageURL); } return String(imageCandidates.last().imageURL); }
String bestFitSourceForImageAttributes(float deviceScaleFactor, const String& srcAttribute, const String& srcsetAttribute) { ImageCandidates imageCandidates; parseImagesWithScaleFromSrcsetAttribute(srcsetAttribute, imageCandidates); if (!srcAttribute.isEmpty()) { ImageWithScale srcPlaceholderImage; imageCandidates.append(srcPlaceholderImage); } if (imageCandidates.isEmpty()) return String(); std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByScaleFactor); for (size_t i = 0; i < imageCandidates.size() - 1; ++i) { if (imageCandidates[i].scaleFactor >= deviceScaleFactor) return imageCandidates[i].hasImageURL() ? srcsetAttribute.substringSharingImpl(imageCandidates[i].imageURLStart, imageCandidates[i].imageURLLength) : srcAttribute; } const ImageWithScale& lastCandidate = imageCandidates.last(); return lastCandidate.hasImageURL() ? srcsetAttribute.substringSharingImpl(lastCandidate.imageURLStart, lastCandidate.imageURLLength) : srcAttribute; }
// See the specifications for more details about the algorithm to follow. // http://www.w3.org/TR/2013/WD-html-srcset-20130228/#processing-the-image-candidates. static void parseImagesWithScaleFromSrcsetAttribute(const String& srcsetAttribute, ImageCandidates& imageCandidates) { ASSERT(imageCandidates.isEmpty()); size_t imageCandidateStart = 0; unsigned srcsetAttributeLength = srcsetAttribute.length(); while (imageCandidateStart < srcsetAttributeLength) { float imageScaleFactor = 1; size_t separator; // 4. Splitting loop: Skip whitespace. size_t imageURLStart = srcsetAttribute.find(isNotHTMLSpace, imageCandidateStart); if (imageURLStart == notFound) break; // If The current candidate is either totally empty or only contains space, skipping. if (srcsetAttribute[imageURLStart] == ',') { imageCandidateStart = imageURLStart + 1; continue; } // 5. Collect a sequence of characters that are not space characters, and let that be url. size_t imageURLEnd = srcsetAttribute.find(isHTMLSpace, imageURLStart + 1); if (imageURLEnd == notFound) { imageURLEnd = srcsetAttributeLength; separator = srcsetAttributeLength; } else if (srcsetAttribute[imageURLEnd - 1] == ',') { --imageURLEnd; separator = imageURLEnd; } else { // 7. Collect a sequence of characters that are not "," (U+002C) characters, and let that be descriptors. size_t imageScaleStart = srcsetAttribute.find(isNotHTMLSpace, imageURLEnd + 1); if (imageScaleStart == notFound) separator = srcsetAttributeLength; else if (srcsetAttribute[imageScaleStart] == ',') separator = imageScaleStart; else { // This part differs from the spec as the current implementation only supports pixel density descriptors for now. size_t imageScaleEnd = srcsetAttribute.find(isHTMLSpaceOrComma, imageScaleStart + 1); imageScaleEnd = (imageScaleEnd == notFound) ? srcsetAttributeLength : imageScaleEnd; size_t commaPosition = imageScaleEnd; // Make sure there are no other descriptors. while ((commaPosition < srcsetAttributeLength - 1) && isHTMLSpace(srcsetAttribute[commaPosition])) ++commaPosition; // If the first not html space character after the scale modifier is not a comma, // the current candidate is an invalid input. if ((commaPosition < srcsetAttributeLength - 1) && srcsetAttribute[commaPosition] != ',') { // Find the nearest comma and skip the input. commaPosition = srcsetAttribute.find(',', commaPosition + 1); if (commaPosition == notFound) break; imageCandidateStart = commaPosition + 1; continue; } separator = commaPosition; if (srcsetAttribute[imageScaleEnd - 1] != 'x') { imageCandidateStart = separator + 1; continue; } bool validScaleFactor = false; size_t scaleFactorLengthWithoutUnit = imageScaleEnd - imageScaleStart - 1; imageScaleFactor = charactersToFloat(srcsetAttribute.characters() + imageScaleStart, scaleFactorLengthWithoutUnit, &validScaleFactor); if (!validScaleFactor) { imageCandidateStart = separator + 1; continue; } } } ImageWithScale image; image.imageURL = String(srcsetAttribute.characters() + imageURLStart, imageURLEnd - imageURLStart); image.scaleFactor = imageScaleFactor; imageCandidates.append(image); // 11. Return to the step labeled splitting loop. imageCandidateStart = separator + 1; } }