void HTMLImageElement::selectSourceURL(ImageLoader::UpdateFromElementBehavior behavior) { if (!document().isActive()) return; bool foundURL = false; if (RuntimeEnabledFeatures::pictureEnabled()) { ImageCandidate candidate = findBestFitImageFromPictureParent(); if (!candidate.isEmpty()) { setBestFitURLAndDPRFromImageCandidate(candidate); foundURL = true; } } if (!foundURL) { float effectiveSize = 0; if (RuntimeEnabledFeatures::pictureSizesEnabled()) { String sizes = fastGetAttribute(sizesAttr); if (!sizes.isNull()) UseCounter::count(document(), UseCounter::Sizes); SizesAttributeParser parser = SizesAttributeParser(MediaValuesDynamic::create(document()), sizes); effectiveSize = parser.length(); } ImageCandidate candidate = bestFitSourceForImageAttributes(document().devicePixelRatio(), effectiveSize, fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr), &document()); setBestFitURLAndDPRFromImageCandidate(candidate); } if (m_intrinsicSizingViewportDependant && !m_listener) { m_listener = ViewportChangeListener::create(this); document().mediaQueryMatcher().addViewportListener(m_listener); } imageLoader().updateFromElement(behavior); }
void HTMLImageElement::selectSourceURL(ImageLoader::UpdateFromElementBehavior behavior) { if (!document().isActive()) return; bool foundURL = false; ImageCandidate candidate = findBestFitImageFromPictureParent(); if (!candidate.isEmpty()) { setBestFitURLAndDPRFromImageCandidate(candidate); foundURL = true; } if (!foundURL) { candidate = bestFitSourceForImageAttributes(document().devicePixelRatio(), sourceSize(*this), fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr), &document()); setBestFitURLAndDPRFromImageCandidate(candidate); } if (m_intrinsicSizingViewportDependant && !m_listener) { m_listener = ViewportChangeListener::create(this); document().mediaQueryMatcher().addViewportListener(m_listener); } imageLoader().updateFromElement(behavior, m_referrerPolicy); if (imageLoader().image() || (imageLoader().hasPendingActivity() && !imageSourceURL().isEmpty())) ensurePrimaryContent(); else ensureFallbackContent(); }
// http://picture.responsiveimages.org/#update-source-set ImageCandidate HTMLImageElement::findBestFitImageFromPictureParent() { ASSERT(isMainThread()); Node* parent = parentNode(); if (!parent || !isHTMLPictureElement(*parent)) return ImageCandidate(); for (Node* child = parent->firstChild(); child; child = child->nextSibling()) { if (child == this) return ImageCandidate(); if (!isHTMLSourceElement(*child)) continue; HTMLSourceElement* source = toHTMLSourceElement(child); if (!source->fastGetAttribute(srcAttr).isNull()) UseCounter::countDeprecation(document(), UseCounter::PictureSourceSrc); String srcset = source->fastGetAttribute(srcsetAttr); if (srcset.isEmpty()) continue; String type = source->fastGetAttribute(typeAttr); if (!type.isEmpty() && !supportedImageType(type)) continue; if (!source->mediaQueryMatches()) continue; ImageCandidate candidate = bestFitSourceForSrcsetAttribute(document().devicePixelRatio(), sourceSize(*source), source->fastGetAttribute(srcsetAttr), &document()); if (candidate.isEmpty()) continue; return candidate; } return ImageCandidate(); }
void processAttributes(const HTMLToken::AttributeList& attributes, Document& document, Vector<bool>& pictureState) { ASSERT(isMainThread()); if (m_tagId >= TagId::Unknown) return; for (auto& attribute : attributes) { AtomicString attributeName(attribute.name); String attributeValue = StringImpl::create8BitIfPossible(attribute.value); processAttribute(attributeName, attributeValue, document, pictureState); } if (m_tagId == TagId::Source && !pictureState.isEmpty() && !pictureState.last() && m_mediaMatched && !m_srcSetAttribute.isEmpty()) { float sourceSize = parseSizesAttribute(m_sizesAttribute, document.renderView(), document.frame()); ImageCandidate imageCandidate = bestFitSourceForImageAttributes(m_deviceScaleFactor, m_urlToLoad, m_srcSetAttribute, sourceSize); if (!imageCandidate.isEmpty()) { pictureState.last() = true; setUrlToLoad(imageCandidate.string.toString(), true); } } // Resolve between src and srcSet if we have them and the tag is img. if (m_tagId == TagId::Img && !m_srcSetAttribute.isEmpty()) { float sourceSize = parseSizesAttribute(m_sizesAttribute, document.renderView(), document.frame()); ImageCandidate imageCandidate = bestFitSourceForImageAttributes(m_deviceScaleFactor, m_urlToLoad, m_srcSetAttribute, sourceSize); setUrlToLoad(imageCandidate.string.toString(), true); } if (m_metaIsViewport && !m_metaContent.isNull()) document.processViewport(m_metaContent, ViewportArguments::ViewportMeta); }
TEST(ImageCandidateTest, Basic) { ImageCandidate candidate; ASSERT_EQ(candidate.density(), 1); ASSERT_EQ(candidate.getResourceWidth(), -1); ASSERT_EQ(candidate.srcOrigin(), false); }
void HTMLImageElement::setBestFitURLAndDPRFromImageCandidate(const ImageCandidate& candidate) { m_bestFitImageURL = candidate.url(); float candidateDensity = candidate.density(); if (candidateDensity >= 0) m_imageDevicePixelRatio = 1.0 / candidateDensity; if (renderer() && renderer()->isImage()) toRenderImage(renderer())->setImageDevicePixelRatio(m_imageDevicePixelRatio); }
void HTMLImageElement::selectImageSource() { // First look for the best fit source from our <picture> parent if we have one. ImageCandidate candidate = bestFitSourceFromPictureElement(); if (candidate.isEmpty()) { // If we don't have a <picture> or didn't find a source, then we use our own attributes. float sourceSize = parseSizesAttribute(fastGetAttribute(sizesAttr).string(), document().renderView(), document().frame()); candidate = bestFitSourceForImageAttributes(document().deviceScaleFactor(), fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr), sourceSize); } setBestFitURLAndDPRFromImageCandidate(candidate); m_imageLoader.updateFromElementIgnoringPreviousError(); }
void HTMLImageElement::setBestFitURLAndDPRFromImageCandidate(const ImageCandidate& candidate) { m_bestFitImageURL = candidate.url(); float candidateDensity = candidate.density(); if (candidateDensity >= 0) m_imageDevicePixelRatio = 1.0 / candidateDensity; if (candidate.resourceWidth() > 0) { m_intrinsicSizingViewportDependant = true; UseCounter::count(document(), UseCounter::SrcsetWDescriptor); } else if (!candidate.srcOrigin()) { UseCounter::count(document(), UseCounter::SrcsetXDescriptor); } if (layoutObject() && layoutObject()->isImage()) toLayoutImage(layoutObject())->setImageDevicePixelRatio(m_imageDevicePixelRatio); }
Node::InsertionNotificationRequest HTMLImageElement::insertedInto(ContainerNode* insertionPoint) { if (!m_formWasSetByParser || insertionPoint->highestAncestorOrSelf() != m_form->highestAncestorOrSelf()) resetFormOwner(); bool imageWasModified = false; if (RuntimeEnabledFeatures::pictureEnabled()) { ImageCandidate candidate = findBestFitImageFromPictureParent(); if (!candidate.isEmpty()) { setBestFitURLAndDPRFromImageCandidate(candidate); imageWasModified = true; } } // If we have been inserted from a renderer-less document, // our loader may have not fetched the image, so do it now. if ((insertionPoint->inDocument() && !imageLoader().image()) || imageWasModified) imageLoader().updateFromElement(m_elementCreatedByParser ? ImageLoader::ForceLoadImmediately : ImageLoader::LoadNormally); return HTMLElement::insertedInto(insertionPoint); }
String bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned 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(); }
ImageCandidate HTMLImageElement::bestFitSourceFromPictureElement() { auto* parent = parentNode(); if (!is<HTMLPictureElement>(parent)) return { }; auto* picture = downcast<HTMLPictureElement>(parent); picture->clearViewportDependentResults(); document().removeViewportDependentPicture(*picture); for (Node* child = parent->firstChild(); child && child != this; child = child->nextSibling()) { if (!is<HTMLSourceElement>(*child)) continue; auto& source = downcast<HTMLSourceElement>(*child); auto& srcset = source.fastGetAttribute(srcsetAttr); if (srcset.isEmpty()) continue; if (source.hasAttribute(typeAttr)) { String type = source.fastGetAttribute(typeAttr).string(); int indexOfSemicolon = type.find(';'); if (indexOfSemicolon >= 0) type.truncate(indexOfSemicolon); type = stripLeadingAndTrailingHTMLSpaces(type); type = type.lower(); if (!type.isEmpty() && !MIMETypeRegistry::isSupportedImageMIMEType(type) && type != "image/svg+xml") continue; } MediaQueryEvaluator evaluator(document().printing() ? "print" : "screen", document().frame(), computedStyle()); bool evaluation = evaluator.evalCheckingViewportDependentResults(source.mediaQuerySet(), picture->viewportDependentResults()); if (picture->hasViewportDependentResults()) document().addViewportDependentPicture(*picture); if (!evaluation) continue; float sourceSize = parseSizesAttribute(source.fastGetAttribute(sizesAttr).string(), document().renderView(), document().frame()); ImageCandidate candidate = bestFitSourceForImageAttributes(document().deviceScaleFactor(), nullAtom, source.fastGetAttribute(srcsetAttr), sourceSize); if (!candidate.isEmpty()) return candidate; } return { }; }
Node::InsertionNotificationRequest HTMLImageElement::insertedInto(ContainerNode* insertionPoint) { if (!m_formWasSetByParser || NodeTraversal::highestAncestorOrSelf(*insertionPoint) != NodeTraversal::highestAncestorOrSelf(*m_form.get())) resetFormOwner(); if (m_listener) document().mediaQueryMatcher().addViewportListener(m_listener); bool imageWasModified = false; if (document().isActive()) { ImageCandidate candidate = findBestFitImageFromPictureParent(); if (!candidate.isEmpty()) { setBestFitURLAndDPRFromImageCandidate(candidate); imageWasModified = true; } } // If we have been inserted from a layoutObject-less document, // our loader may have not fetched the image, so do it now. if ((insertionPoint->inDocument() && !imageLoader().image()) || imageWasModified) imageLoader().updateFromElement(ImageLoader::UpdateNormal, m_referrerPolicy); return HTMLElement::insertedInto(insertionPoint); }
void HTMLImageElement::selectSourceURL(UpdateFromElementBehavior behavior) { bool foundURL = false; if (RuntimeEnabledFeatures::pictureEnabled()) { ImageCandidate candidate = findBestFitImageFromPictureParent(); if (!candidate.isEmpty()) { setBestFitURLAndDPRFromImageCandidate(candidate); foundURL = true; } } if (!foundURL) { unsigned effectiveSize = 0; if (RuntimeEnabledFeatures::pictureSizesEnabled()) effectiveSize = SizesAttributeParser::findEffectiveSize(fastGetAttribute(sizesAttr), MediaValuesCached::create(document())); ImageCandidate candidate = bestFitSourceForImageAttributes(document().devicePixelRatio(), effectiveSize, fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr)); setBestFitURLAndDPRFromImageCandidate(candidate); } if (behavior == UpdateIgnorePreviousError) imageLoader().updateFromElementIgnoringPreviousError(); else imageLoader().updateFromElement(); }
// http://picture.responsiveimages.org/#update-source-set ImageCandidate HTMLImageElement::findBestFitImageFromPictureParent() { ASSERT(isMainThread()); Node* parent = parentNode(); if (!parent || !isHTMLPictureElement(*parent)) return ImageCandidate(); for (Node* child = parent->firstChild(); child; child = child->nextSibling()) { if (child == this) return ImageCandidate(); if (!isHTMLSourceElement(*child)) continue; HTMLSourceElement* source = toHTMLSourceElement(child); String srcset = source->fastGetAttribute(srcsetAttr); if (srcset.isEmpty()) continue; String type = source->fastGetAttribute(typeAttr); if (!type.isEmpty() && !supportedImageType(type)) continue; String media = source->fastGetAttribute(mediaAttr); if (!media.isEmpty()) { RefPtrWillBeRawPtr<MediaQuerySet> mediaQueries = MediaQuerySet::create(media); if (!document().mediaQueryMatcher().evaluate(mediaQueries.get())) continue; } unsigned effectiveSize = SizesAttributeParser::findEffectiveSize(source->fastGetAttribute(sizesAttr), MediaValuesCached::create(document())); ImageCandidate candidate = bestFitSourceForSrcsetAttribute(document().devicePixelRatio(), effectiveSize, source->fastGetAttribute(srcsetAttr)); if (candidate.isEmpty()) continue; return candidate; } return ImageCandidate(); }
static bool compareByDensity(const ImageCandidate& first, const ImageCandidate& second) { return first.density() < second.density(); }
TEST(HTMLSrcsetParserTest, Basic) { TestCase testCases[] = { {2.0, 0.5, "", "data:,a 1w, data:,b 2x", "data:,a", 2.0, 1}, {2.0, 1, "", "data:,a 2w, data:,b 2x", "data:,a", 2.0, 2}, {2.0, -1, "", "1x.gif 1x, 2x.gif 2x", "2x.gif", 2.0, -1}, {2.0, -1, "", "1x.gif 1q, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "", "1x.gif 1q, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "", "1x.gif 1x 100h, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "", "1x.gif 1x 100w, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "", "1x.gif 1x 100h 100w, 2x.gif 2x", "2x.gif", 2.0, -1}, {2.0, -1, "", "1x.gif 1x, 2x.gif -2x", "1x.gif", 1.0, -1}, {2.0, -1, "", "0x.gif 0x", "0x.gif", 0.0, -1}, {2.0, -1, "", "0x.gif -0x", "0x.gif", 0.0, -1}, {2.0, -1, "", "neg.gif -2x", "", 1.0, -1}, {2.0, -1, "", "1x.gif 1x, 2x.gif 2q", "1x.gif", 1.0, -1}, {2.0, -1, "", "1x.gif, 2x.gif 2q", "1x.gif", 1.0, -1}, {2.0, -1, "", "1x.gif , 2x.gif 2q", "1x.gif", 1.0, -1}, {2.0, -1, "1x.gif 1x, 2x.gif 2x", "1x.gif 1x, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "1x.gif 1x, 2x.gif 2x", "1x.gif 1x, 2x.gif 2x", "1x.gif", 1.0, -1}, {1.0, -1, "1x.gif 1x, 2x.gif 2x", "", "1x.gif 1x, 2x.gif 2x", 1.0, -1}, {2.0, -1, "src.gif", "1x.gif 1x, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "src.gif", "1x.gif 1x, 2x.gif 2x", "1x.gif", 1.0, -1}, {1.0, -1, "src.gif", "2x.gif 2x", "src.gif", 1.0, -1}, {2.0, -1, "src.gif", "2x.gif 2x", "2x.gif", 2.0, -1}, {2.0, -1, "src.gif", "2x.gif 2px", "src.gif", 1.0, -1}, {2.0, -1, "src.gif", "2x.gif 2ex", "src.gif", 1.0, -1}, {10.0, -1, "src.gif", "2x.gif 2e1x", "2x.gif", 20.0, -1}, {2.0, -1, "src.gif", "2x.gif 2e1x", "src.gif", 1.0, -1}, {2.0, -1, "src.gif", "2x.gif +2x", "src.gif", 1.0, -1}, {1.5, -1, "src.gif", "2x.gif 2x", "2x.gif", 2.0, -1}, {2.5, -1, "src.gif", "2x.gif 2x", "2x.gif", 2.0, -1}, {2.5, -1, "src.gif", "2x.gif 2x, 3x.gif 3x", "3x.gif", 3.0, -1}, {2.0, -1, "", "1x,, , x ,2x ", "1x", 1.0, -1}, {2.0, -1, "", "1x,, , x ,2x ", "1x", 1.0, -1}, {2.0, -1, "", ",,1x,, , x ,2x ", "1x", 1.0, -1}, {2.0, -1, "", ",,1x,,", "1x", 1.0, -1}, {2.0, -1, "", ",1x,", "1x", 1.0, -1}, {2.0, -1, "", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg 1x, 2x.gif 2x", "2x.gif", 2.0, -1}, {2.0, -1, "", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg 2x, 1x.gif 1x", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg", 2.0, -1}, {2.0, -1, "", "1x,, , x ,2x , 1x.gif, 3x, 4x.gif 4x 100z, 5x.gif 5, dx.gif dx, 2x.gif 2x ,", "2x.gif", 2.0, -1}, {4.0, -1, "", "1x,, , x ,2x , 1x.gif, 3x, 4x.gif 4x 100h, 5x.gif 5, dx.gif dx, 2x.gif 2x ,", "2x.gif", 2.0, -1}, {4.0, -1, "", "1x,, , x ,2x , 1x.gif, 3x, 4x.gif 4x 100z, 5x.gif 5, dx.gif dx, 2x.gif 2x ,", "2x.gif", 2.0, -1}, {1.0, -1, "", "1x,, , x ,2x , 1x.gif, 3x, 4x.gif 4x 100z, 5x.gif 5, dx.gif dx, 2x.gif 2x ,", "1x", 1.0, -1}, {5.0, -1, "", "1x,, , x ,2x , 1x.gif, 3x, 4x.gif 4x 100z, 5x.gif 5, dx.gif dx, 2x.gif 2x ,", "2x.gif", 2.0, -1}, {2.0, -1, "", "1x.gif 1x, data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KCTxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJncmVlbiIvPgo8L3N2Zz4K 2x", "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KCTxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJncmVlbiIvPgo8L3N2Zz4K", 2.0, -1 }, {2.0, -1, "1x.gif", "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KCTxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJncmVlbiIvPgo8L3N2Zz4K 2x", "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KCTxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJncmVlbiIvPgo8L3N2Zz4K", 2.0, -1 }, {2.0, -1, "1x.svg#red", "1x.svg#green 2x", "1x.svg#green", 2.0, -1}, {2.0, -1, "", "1x.svg#red 1x, 1x.svg#green 2x", "1x.svg#green", 2.0, -1}, {1.0, 400, "", "400.gif 400w, 6000.gif 6000w", "400.gif", 1.0, 400}, {1.0, 400, "", "400.gif 400pw, 6000.gif 6000w", "6000.gif", 15.0, 6000}, {1.0, 400, "fallback.gif", "400.gif 400pw", "fallback.gif", 1.0, -1}, {1.0, 400, "fallback.gif", "400.gif +400w", "fallback.gif", 1.0, -1}, {1.0, 400, "", "400.gif 400w 400h, 6000.gif 6000w", "400.gif", 1.0, 400}, {4.0, 400, "", "400.gif 400w, 6000.gif 6000w", "6000.gif", 15.0, 6000}, {3.8, 400, "", "400.gif 400w, 6000.gif 6000w", "400.gif", 1.0, 400}, {0.9, 800, "src.gif", "400.gif 400w", "400.gif", 0.5, 400}, {0.9, 800, "src.gif", "1x.gif 1x, 400.gif 400w", "1x.gif", 1.0, -1}, {0.9, 800, "src.gif", "1x.gif 0.6x, 400.gif 400w", "1x.gif", 0.6, -1}, {0.9, 800, "src.gif", "1x.gif 1x, 400.gif 720w", "400.gif", 0.9, 720}, {0.9, 800, "src.gif", "1x.gif 1x, 400.gif 719w", "1x.gif", 1.0, -1}, {2.0, 800, "src.gif", "400.gif 400w", "400.gif", 0.5, 400}, {1.0, 400, "src.gif", "800.gif 800w", "800.gif", 2.0, 800}, {1.0, 400, "src.gif", "0.gif 0w, 800.gif 800w", "800.gif", 2.0, 800}, {1.0, 400, "src.gif", "0.gif 0w, 2x.gif 2x", "src.gif", 1.0, -1}, {1.0, 400, "src.gif", "800.gif 2x, 1600.gif 1600w", "800.gif", 2.0, -1}, {1.0, 400, "", "400.gif 400w, 2x.gif 2x", "400.gif", 1.0, 400}, {2.0, 400, "", "400.gif 400w, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, 0, "", "400.gif 400w, 6000.gif 6000w", "400.gif", std::numeric_limits<float>::infinity(), 400}, {2.0, -1, "", ", 1x.gif 1x, 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "", ",1x.gif 1x, 2x.gif 2x", "1x.gif", 1.0, -1}, {1.0, -1, "", ",1x.gif 1.x , 2x.gif 2x", "2x.gif", 2.0, -1}, {1.2, -1, "", ",1x.gif 1x, 1.4x.gif 1.4x, 2x.gif 2x", "1.4x.gif", 1.4, -1}, {1.0, -1, "", "inf.gif 0.00000000001x", "inf.gif", 1e-11, -1}, {1.0, -1, "", "data:,a ( , data:,b 1x, ), data:,c", "data:,c", 1.0, -1}, {1.0, 1, "", "data:,a 1w 1h", "data:,a", 1.0, 1}, {1.0, -1, "", ",1x.gif 1x future-descriptor(3x, 4h, whatever), 2x.gif 2x", "2x.gif", 2.0, -1}, {2.0, -1, "", ",1x.gif 1x future-descriptor(3x, 4h, whatever), 2x.gif 2x", "2x.gif", 2.0, -1}, {1.0, -1, "", "data:,a 1 w", "", 1.0, -1}, {1.0, -1, "", "data:,a 1 w", "", 1.0, -1}, {1.0, -1, "", "data:,a +1x", "", 1.0, -1}, {1.0, -1, "", "data:,a +1x", "", 1.0, -1}, {1.0, -1, "", "data:,a 1.0x", "data:,a", 1.0, -1}, {1.0, -1, "", "1%20and%202.gif 1x", "1%20and%202.gif", 1.0, -1}, {1.0, 700, "", "data:,a 0.5x, data:,b 1400w", "data:,b", 2.0, 1400}, {0, 0, 0, 0, 0, 0} // Do not remove the terminator line. }; for (unsigned i = 0; testCases[i].srcInput; ++i) { TestCase test = testCases[i]; ImageCandidate candidate = bestFitSourceForImageAttributes(test.deviceScaleFactor, test.effectiveSize, test.srcInput, test.srcsetInput); ASSERT_EQ(test.outputDensity, candidate.density()); ASSERT_EQ(test.outputResourceWidth, candidate.getResourceWidth()); ASSERT_STREQ(test.outputURL, candidate.toString().ascii().data()); } }