summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/parser/HTMLSrcsetParser.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/html/parser/HTMLSrcsetParser.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/html/parser/HTMLSrcsetParser.cpp')
-rw-r--r--Source/WebCore/html/parser/HTMLSrcsetParser.cpp276
1 files changed, 276 insertions, 0 deletions
diff --git a/Source/WebCore/html/parser/HTMLSrcsetParser.cpp b/Source/WebCore/html/parser/HTMLSrcsetParser.cpp
new file mode 100644
index 000000000..66a9864c2
--- /dev/null
+++ b/Source/WebCore/html/parser/HTMLSrcsetParser.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HTMLSrcsetParser.h"
+
+#include "HTMLParserIdioms.h"
+#include "ParsingUtilities.h"
+
+namespace WebCore {
+
+static inline bool compareByDensity(const ImageCandidate& first, const ImageCandidate& second)
+{
+ return first.density < second.density;
+}
+
+enum DescriptorTokenizerState {
+ Start,
+ InParenthesis,
+ AfterToken,
+};
+
+template<typename CharType>
+static void appendDescriptorAndReset(const CharType*& descriptorStart, const CharType* position, Vector<StringView>& descriptors)
+{
+ if (position > descriptorStart)
+ descriptors.append(StringView(descriptorStart, position - descriptorStart));
+ descriptorStart = nullptr;
+}
+
+// The following is called appendCharacter to match the spec's terminology.
+template<typename CharType>
+static void appendCharacter(const CharType* descriptorStart, const CharType* 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*& position, const CharType* attributeEnd, Vector<StringView>& descriptors)
+{
+ DescriptorTokenizerState state = Start;
+ const CharType* descriptorsStart = position;
+ const CharType* currentDescriptorStart = descriptorsStart;
+ for (; ; ++position) {
+ switch (state) {
+ case Start:
+ if (isEOF(position, attributeEnd)) {
+ appendDescriptorAndReset(currentDescriptorStart, attributeEnd, descriptors);
+ return;
+ }
+ if (isComma(*position)) {
+ appendDescriptorAndReset(currentDescriptorStart, position, descriptors);
+ ++position;
+ return;
+ }
+ if (isHTMLSpace(*position)) {
+ appendDescriptorAndReset(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(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;
+ }
+ }
+}
+
+static bool parseDescriptors(Vector<StringView>& descriptors, DescriptorParsingResult& result)
+{
+ for (auto& descriptor : descriptors) {
+ if (descriptor.isEmpty())
+ continue;
+ unsigned descriptorCharPosition = descriptor.length() - 1;
+ UChar descriptorChar = descriptor[descriptorCharPosition];
+ descriptor = descriptor.substring(0, descriptorCharPosition);
+ if (descriptorChar == 'x') {
+ if (result.hasDensity() || result.hasHeight() || result.hasWidth())
+ return false;
+ std::optional<double> density = parseValidHTMLFloatingPointNumber(descriptor);
+ if (!density || density.value() < 0)
+ return false;
+ result.setDensity(density.value());
+ } else if (descriptorChar == 'w') {
+ if (result.hasDensity() || result.hasWidth())
+ return false;
+ std::optional<int> resourceWidth = parseValidHTMLNonNegativeInteger(descriptor);
+ if (!resourceWidth || resourceWidth.value() <= 0)
+ return false;
+ result.setResourceWidth(resourceWidth.value());
+ } else if (descriptorChar == '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;
+ std::optional<int> resourceHeight = parseValidHTMLNonNegativeInteger(descriptor);
+ if (!resourceHeight || resourceHeight.value() <= 0)
+ return false;
+ result.setResourceHeight(resourceHeight.value());
+ } else
+ return false;
+ }
+ return !result.hasHeight() || result.hasWidth();
+}
+
+// http://picture.responsiveimages.org/#parse-srcset-attr
+template<typename CharType>
+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 {
+ skipWhile<CharType, isHTMLSpace<CharType>>(position, attributeEnd);
+ 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;
+}
+
+Vector<ImageCandidate> parseImageCandidatesFromSrcsetAttribute(StringView attribute)
+{
+ // FIXME: We should consider replacing the direct pointers in the parsing process with StringView and positions.
+ if (attribute.is8Bit())
+ return parseImageCandidatesFromSrcsetAttribute<LChar>(attribute.characters8(), attribute.length());
+ else
+ return parseImageCandidatesFromSrcsetAttribute<UChar>(attribute.characters16(), attribute.length());
+}
+
+static ImageCandidate pickBestImageCandidate(float deviceScaleFactor, Vector<ImageCandidate>& imageCandidates, float sourceSize)
+{
+ bool ignoreSrc = false;
+ if (imageCandidates.isEmpty())
+ return ImageCandidate();
+
+ // http://picture.responsiveimages.org/#normalize-source-densities
+ for (auto& candidate : imageCandidates) {
+ if (candidate.resourceWidth > 0) {
+ candidate.density = static_cast<float>(candidate.resourceWidth) / sourceSize;
+ ignoreSrc = true;
+ } else 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];
+}
+
+ImageCandidate bestFitSourceForImageAttributes(float deviceScaleFactor, const AtomicString& srcAttribute, const AtomicString& srcsetAttribute, float sourceSize)
+{
+ if (srcsetAttribute.isNull()) {
+ if (srcAttribute.isNull())
+ return ImageCandidate();
+ return ImageCandidate(StringView(srcAttribute), DescriptorParsingResult(), ImageCandidate::SrcOrigin);
+ }
+
+ Vector<ImageCandidate> imageCandidates = parseImageCandidatesFromSrcsetAttribute(StringView(srcsetAttribute));
+
+ if (!srcAttribute.isEmpty())
+ imageCandidates.append(ImageCandidate(StringView(srcAttribute), DescriptorParsingResult(), ImageCandidate::SrcOrigin));
+
+ return pickBestImageCandidate(deviceScaleFactor, imageCandidates, sourceSize);
+}
+
+} // namespace WebCore