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

Unified 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, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/html/parser/HTMLSrcsetParser.h ('k') | Source/core/html/parser/HTMLSrcsetParserTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/html/parser/HTMLSrcsetParser.cpp
diff --git a/Source/core/html/parser/HTMLSrcsetParser.cpp b/Source/core/html/parser/HTMLSrcsetParser.cpp
index 9b1e5459fb54b36cbe9b1a6337b0113482054865..9725e96bdeaa9396b6600eaac69c36680ad01ad9 100644
--- a/Source/core/html/parser/HTMLSrcsetParser.cpp
+++ b/Source/core/html/parser/HTMLSrcsetParser.cpp
@@ -38,54 +38,172 @@
namespace WebCore {
-static bool compareByScaleFactor(const ImageCandidate& first, const ImageCandidate& second)
+static bool compareByDensity(const ImageCandidate& first, const ImageCandidate& second)
{
- return first.scaleFactor() < second.scaleFactor();
+ return first.density() < second.density();
}
+enum DescriptorTokenizerState {
+ Start,
+ InParenthesis,
+ AfterToken,
+};
+
+struct DescriptorToken {
+ unsigned start;
+ unsigned length;
+
+ DescriptorToken(unsigned start, unsigned length)
+ : start(start)
+ , length(length)
+ {
+ }
+
+ unsigned lastIndex()
+ {
+ return start + length - 1;
+ }
+
+ template<typename CharType>
+ int toInt(const CharType* attribute, bool& isValid)
+ {
+ return charactersToInt(attribute + start, length - 1, &isValid);
+ }
+
+ template<typename CharType>
+ float toFloat(const CharType* attribute, bool& isValid)
+ {
+ return charactersToFloat(attribute + start, length - 1, &isValid);
+ }
+};
+
template<typename CharType>
-inline bool isComma(CharType character)
+static void appendDescriptorAndReset(const CharType* attributeStart, const CharType*& descriptorStart, const CharType* position, Vector<DescriptorToken>& descriptors)
{
- return character == ',';
+ if (position > descriptorStart)
+ descriptors.append(DescriptorToken(descriptorStart - attributeStart, position - descriptorStart));
+ descriptorStart = 0;
}
+// The following is called appendCharacter to match the spec's terminology.
template<typename CharType>
-static bool parseDescriptors(const CharType* descriptorsStart, const CharType* descriptorsEnd, DescriptorParsingResult& result)
+static void appendCharacter(const CharType* descriptorStart, const CharType* position)
{
- const CharType* position = descriptorsStart;
- bool isValid = false;
- bool isEmptyDescriptor = !(descriptorsEnd > descriptorsStart);
- while (position < descriptorsEnd) {
- // 13.1. Let descriptor list be the result of splitting unparsed descriptors on spaces.
- skipWhile<CharType, isHTMLSpace<CharType> >(position, descriptorsEnd);
- const CharType* currentDescriptorStart = position;
- skipWhile<CharType, isNotHTMLSpace<CharType> >(position, descriptorsEnd);
- const CharType* currentDescriptorEnd = position;
+ // Since we don't copy the tokens, this just set the point where the descriptor tokens start.
+ if (!descriptorStart)
+ descriptorStart = position;
+}
+template<typename CharType>
+static bool isEOF(const CharType* position, const CharType* end)
+{
+ return position >= end;
+}
+
+template<typename CharType>
+static void tokenizeDescriptors(const CharType* attributeStart,
+ const CharType*& position,
+ const CharType* attributeEnd,
+ Vector<DescriptorToken>& descriptors)
+{
+ DescriptorTokenizerState state = Start;
+ const CharType* descriptorsStart = position;
+ const CharType* currentDescriptorStart = descriptorsStart;
+ while (true) {
+ switch (state) {
+ case Start:
+ if (isEOF(position, attributeEnd)) {
+ appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors);
+ return;
+ }
+ if (isComma(*position)) {
+ appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors);
+ ++position;
+ return;
+ }
+ if (isHTMLSpace(*position)) {
+ appendDescriptorAndReset(attributeStart, currentDescriptorStart, position, descriptors);
+ currentDescriptorStart = position + 1;
+ state = AfterToken;
+ } else if (*position == '(') {
+ appendCharacter(currentDescriptorStart, position);
+ state = InParenthesis;
+ } else {
+ appendCharacter(currentDescriptorStart, position);
+ }
+ break;
+ case InParenthesis:
+ if (isEOF(position, attributeEnd)) {
+ appendDescriptorAndReset(attributeStart, currentDescriptorStart, attributeEnd, descriptors);
+ return;
+ }
+ if (*position == ')') {
+ appendCharacter(currentDescriptorStart, position);
+ state = Start;
+ } else {
+ appendCharacter(currentDescriptorStart, position);
+ }
+ break;
+ case AfterToken:
+ if (isEOF(position, attributeEnd))
+ return;
+ if (!isHTMLSpace(*position)) {
+ state = Start;
+ currentDescriptorStart = position;
+ --position;
+ }
+ break;
+ }
++position;
- ASSERT(currentDescriptorEnd > currentDescriptorStart);
- --currentDescriptorEnd;
- unsigned descriptorLength = currentDescriptorEnd - currentDescriptorStart;
- if (*currentDescriptorEnd == 'x') {
- if (result.foundDescriptor())
+ }
+}
+
+template<typename CharType>
+static bool parseDescriptors(const CharType* attribute, Vector<DescriptorToken>& descriptors, DescriptorParsingResult& result)
+{
+ for (Vector<DescriptorToken>::iterator it = descriptors.begin(); it != descriptors.end(); ++it) {
+ if (it->length == 0)
+ continue;
+ CharType c = attribute[it->lastIndex()];
+ bool isValid = false;
+ if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'w') {
+ if (result.hasDensity() || result.hasWidth())
+ return false;
+ int resourceWidth = it->toInt(attribute, isValid);
+ if (!isValid || resourceWidth <= 0)
+ return false;
+ result.setResourceWidth(resourceWidth);
+ } else if (RuntimeEnabledFeatures::pictureSizesEnabled() && c == 'h') {
+ // This is here only for future compat purposes.
+ // The value of the 'h' descriptor is not used.
+ if (result.hasDensity() || result.hasHeight())
return false;
- result.scaleFactor = charactersToFloat(currentDescriptorStart, descriptorLength, &isValid);
- if (!isValid || result.scaleFactor < 0)
+ int resourceHeight = it->toInt(attribute, isValid);
+ if (!isValid || resourceHeight <= 0)
return false;
- } else if (RuntimeEnabledFeatures::pictureSizesEnabled() && *currentDescriptorEnd == 'w') {
- if (result.foundDescriptor())
+ result.setResourceHeight(resourceHeight);
+ } else if (c == 'x') {
+ if (result.hasDensity() || result.hasHeight() || result.hasWidth())
return false;
- result.resourceWidth = charactersToInt(currentDescriptorStart, descriptorLength, &isValid);
- if (!isValid || result.resourceWidth <= 0)
+ int density = it->toFloat(attribute, isValid);
+ if (!isValid || density < 0)
return false;
+ result.setDensity(density);
}
}
- if (isEmptyDescriptor)
- result.scaleFactor = 1.0;
- return result.foundDescriptor();
+ return true;
}
-// http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#processing-the-image-candidates
+static bool parseDescriptors(const String& attribute, Vector<DescriptorToken>& descriptors, DescriptorParsingResult& result)
+{
+ // FIXME: See if StringView can't be extended to replace DescriptorToken here.
+ if (attribute.is8Bit()) {
+ return parseDescriptors(attribute.characters8(), descriptors, result);
+ }
+ return parseDescriptors(attribute.characters16(), descriptors, result);
+}
+
+// http://picture.responsiveimages.org/#parse-srcset-attr
template<typename CharType>
static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, const CharType* attributeStart, unsigned length, Vector<ImageCandidate>& imageCandidates)
{
@@ -93,33 +211,38 @@ static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, con
const CharType* attributeEnd = position + length;
while (position < attributeEnd) {
- DescriptorParsingResult result;
- // 4. Splitting loop: Skip whitespace.
- skipWhile<CharType, isHTMLSpace<CharType> >(position, attributeEnd);
- if (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;
-
- // If The current candidate is either totally empty or only contains space, skipping.
- if (*position == ',') {
- ++position;
- continue;
}
+ const CharType* imageURLStart = position;
+ // 6. Collect a sequence of characters that are not space characters, and let that be url.
- // 5. 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;
- if (position != attributeEnd && *(position - 1) == ',') {
- --imageURLEnd;
- result.scaleFactor = 1.0;
+ 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 {
- // 7. Collect a sequence of characters that are not "," (U+002C) characters, and let that be descriptors.
- skipWhile<CharType, isHTMLSpace<CharType> >(position, attributeEnd);
- const CharType* descriptorsStart = position;
- skipUntil<CharType, isComma<CharType> >(position, attributeEnd);
- const CharType* descriptorsEnd = position;
- if (!parseDescriptors(descriptorsStart, descriptorsEnd, result))
+ // 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<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))
continue;
}
@@ -145,6 +268,7 @@ static void parseImageCandidatesFromSrcsetAttribute(const String& attribute, Vec
static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, unsigned sourceSize, Vector<ImageCandidate>& imageCandidates)
{
+ const float defaultDensityValue = 1.0;
bool ignoreSrc = false;
if (imageCandidates.isEmpty())
return ImageCandidate();
@@ -152,16 +276,18 @@ static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, unsigned s
// http://picture.responsiveimages.org/#normalize-source-densities
for (Vector<ImageCandidate>::iterator it = imageCandidates.begin(); it != imageCandidates.end(); ++it) {
if (it->resourceWidth() > 0) {
- it->setScaleFactor((float)it->resourceWidth() / (float)sourceSize);
+ it->setDensity((float)it->resourceWidth() / (float)sourceSize);
ignoreSrc = true;
+ } else if (it->density() < 0) {
+ it->setDensity(defaultDensityValue);
}
}
- std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByScaleFactor);
+ std::stable_sort(imageCandidates.begin(), imageCandidates.end(), compareByDensity);
unsigned i;
for (i = 0; i < imageCandidates.size() - 1; ++i) {
- if ((imageCandidates[i].scaleFactor() >= deviceScaleFactor) && (!ignoreSrc || !imageCandidates[i].srcOrigin()))
+ if ((imageCandidates[i].density() >= deviceScaleFactor) && (!ignoreSrc || !imageCandidates[i].srcOrigin()))
break;
}
@@ -169,12 +295,12 @@ static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, unsigned s
ASSERT(i > 0);
--i;
}
- float winningScaleFactor = imageCandidates[i].scaleFactor();
+ 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].scaleFactor() == winningScaleFactor))
+ while ((i > 0) && (imageCandidates[--i].density() == winningDensity))
winner = i;
return imageCandidates[winner];
@@ -191,13 +317,10 @@ ImageCandidate bestFitSourceForSrcsetAttribute(float deviceScaleFactor, unsigned
ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceSize, const String& srcAttribute, const String& srcsetAttribute)
{
- DescriptorParsingResult defaultResult;
- defaultResult.scaleFactor = 1.0;
-
if (srcsetAttribute.isNull()) {
if (srcAttribute.isNull())
return ImageCandidate();
- return ImageCandidate(srcAttribute, 0, srcAttribute.length(), defaultResult, ImageCandidate::SrcOrigin);
+ return ImageCandidate(srcAttribute, 0, srcAttribute.length(), DescriptorParsingResult(), ImageCandidate::SrcOrigin);
}
Vector<ImageCandidate> imageCandidates;
@@ -205,16 +328,13 @@ ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned
parseImageCandidatesFromSrcsetAttribute(srcsetAttribute, imageCandidates);
if (!srcAttribute.isEmpty())
- imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.length(), defaultResult, ImageCandidate::SrcOrigin));
+ imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.length(), DescriptorParsingResult(), ImageCandidate::SrcOrigin));
return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates);
}
String bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceSize, const String& srcAttribute, ImageCandidate& srcsetImageCandidate)
{
- DescriptorParsingResult defaultResult;
- defaultResult.scaleFactor = 1.0;
-
if (srcsetImageCandidate.isEmpty())
return srcAttribute;
@@ -222,7 +342,7 @@ String bestFitSourceForImageAttributes(float deviceScaleFactor, unsigned sourceS
imageCandidates.append(srcsetImageCandidate);
if (!srcAttribute.isEmpty())
- imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.length(), defaultResult, ImageCandidate::SrcOrigin));
+ imageCandidates.append(ImageCandidate(srcAttribute, 0, srcAttribute.length(), DescriptorParsingResult(), ImageCandidate::SrcOrigin));
return pickBestImageCandidate(deviceScaleFactor, sourceSize, imageCandidates).toString();
}
« 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