summaryrefslogtreecommitdiff
path: root/Source/WebCore/loader/LinkHeader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/loader/LinkHeader.cpp')
-rw-r--r--Source/WebCore/loader/LinkHeader.cpp365
1 files changed, 365 insertions, 0 deletions
diff --git a/Source/WebCore/loader/LinkHeader.cpp b/Source/WebCore/loader/LinkHeader.cpp
new file mode 100644
index 000000000..e2546d688
--- /dev/null
+++ b/Source/WebCore/loader/LinkHeader.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2015 The Chromium Authors. All rights reserved.
+ * Copyright (C) 2016 Akamai Technologies 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * 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 "LinkHeader.h"
+
+#include "ParsingUtilities.h"
+
+namespace WebCore {
+
+// LWSP definition in https://www.ietf.org/rfc/rfc0822.txt
+template <typename CharacterType>
+static bool isSpaceOrTab(CharacterType chr)
+{
+ return (chr == ' ') || (chr == '\t');
+}
+
+template <typename CharacterType>
+static bool isNotURLTerminatingChar(CharacterType chr)
+{
+ return (chr != '>');
+}
+
+template <typename CharacterType>
+static bool isValidParameterNameChar(CharacterType chr)
+{
+ // A separator, CTL or '%', '*' or '\'' means the char is not valid.
+ // Definition as attr-char at https://tools.ietf.org/html/rfc5987
+ // CTL and separators are defined in https://tools.ietf.org/html/rfc2616#section-2.2
+ // Valid chars are alpha-numeric and any of !#$&+-.^_`|~"
+ if ((chr >= '^' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || (chr >= '!' && chr <= '$') || chr == '&' || chr == '+' || chr == '-' || chr == '.')
+ return true;
+ return false;
+}
+
+template <typename CharacterType>
+static bool isParameterValueEnd(CharacterType chr)
+{
+ return chr == ';' || chr == ',';
+}
+
+template <typename CharacterType>
+static bool isParameterValueChar(CharacterType chr)
+{
+ return !isSpaceOrTab(chr) && !isParameterValueEnd(chr);
+}
+
+// Verify that the parameter is a link-extension which according to spec doesn't have to have a value.
+static bool isExtensionParameter(LinkHeader::LinkParameterName name)
+{
+ return name >= LinkHeader::LinkParameterUnknown;
+}
+
+// Before:
+//
+// <cat.jpg>; rel=preload
+// ^ ^
+// position end
+//
+// After (if successful: otherwise the method returns false)
+//
+// <cat.jpg>; rel=preload
+// ^ ^
+// position end
+template <typename CharacterType>
+static std::optional<String> findURLBoundaries(CharacterType*& position, CharacterType* const end)
+{
+ ASSERT(position <= end);
+ skipWhile<CharacterType, isSpaceOrTab>(position, end);
+ if (!skipExactly<CharacterType>(position, end, '<'))
+ return std::nullopt;
+ skipWhile<CharacterType, isSpaceOrTab>(position, end);
+
+ CharacterType* urlStart = position;
+ skipWhile<CharacterType, isNotURLTerminatingChar>(position, end);
+ CharacterType* urlEnd = position;
+ skipUntil<CharacterType>(position, end, '>');
+ if (!skipExactly<CharacterType>(position, end, '>'))
+ return std::nullopt;
+
+ return String(urlStart, urlEnd - urlStart);
+}
+
+template <typename CharacterType>
+static bool invalidParameterDelimiter(CharacterType*& position, CharacterType* const end)
+{
+ ASSERT(position <= end);
+ return (!skipExactly<CharacterType>(position, end, ';') && (position < end) && (*position != ','));
+}
+
+template <typename CharacterType>
+static bool validFieldEnd(CharacterType*& position, CharacterType* const end)
+{
+ ASSERT(position <= end);
+ return (position == end || *position == ',');
+}
+
+// Before:
+//
+// <cat.jpg>; rel=preload
+// ^ ^
+// position end
+//
+// After (if successful: otherwise the method returns false, and modifies the isValid boolean accordingly)
+//
+// <cat.jpg>; rel=preload
+// ^ ^
+// position end
+template <typename CharacterType>
+static bool parseParameterDelimiter(CharacterType*& position, CharacterType* const end, bool& isValid)
+{
+ ASSERT(position <= end);
+ isValid = true;
+ skipWhile<CharacterType, isSpaceOrTab>(position, end);
+ if (invalidParameterDelimiter(position, end)) {
+ isValid = false;
+ return false;
+ }
+ skipWhile<CharacterType, isSpaceOrTab>(position, end);
+ if (validFieldEnd(position, end))
+ return false;
+ return true;
+}
+
+static LinkHeader::LinkParameterName paramterNameFromString(String name)
+{
+ if (equalLettersIgnoringASCIICase(name, "rel"))
+ return LinkHeader::LinkParameterRel;
+ if (equalLettersIgnoringASCIICase(name, "anchor"))
+ return LinkHeader::LinkParameterAnchor;
+ if (equalLettersIgnoringASCIICase(name, "crossorigin"))
+ return LinkHeader::LinkParameterCrossOrigin;
+ if (equalLettersIgnoringASCIICase(name, "title"))
+ return LinkHeader::LinkParameterTitle;
+ if (equalLettersIgnoringASCIICase(name, "media"))
+ return LinkHeader::LinkParameterMedia;
+ if (equalLettersIgnoringASCIICase(name, "type"))
+ return LinkHeader::LinkParameterType;
+ if (equalLettersIgnoringASCIICase(name, "rev"))
+ return LinkHeader::LinkParameterRev;
+ if (equalLettersIgnoringASCIICase(name, "hreflang"))
+ return LinkHeader::LinkParameterHreflang;
+ if (equalLettersIgnoringASCIICase(name, "as"))
+ return LinkHeader::LinkParameterAs;
+ return LinkHeader::LinkParameterUnknown;
+}
+
+// Before:
+//
+// <cat.jpg>; rel=preload
+// ^ ^
+// position end
+//
+// After (if successful: otherwise the method returns false)
+//
+// <cat.jpg>; rel=preload
+// ^ ^
+// position end
+template <typename CharacterType>
+static bool parseParameterName(CharacterType*& position, CharacterType* const end, LinkHeader::LinkParameterName& name)
+{
+ ASSERT(position <= end);
+ CharacterType* nameStart = position;
+ skipWhile<CharacterType, isValidParameterNameChar>(position, end);
+ CharacterType* nameEnd = position;
+ skipWhile<CharacterType, isSpaceOrTab>(position, end);
+ bool hasEqual = skipExactly<CharacterType>(position, end, '=');
+ skipWhile<CharacterType, isSpaceOrTab>(position, end);
+ name = paramterNameFromString(String(nameStart, nameEnd - nameStart));
+ if (hasEqual)
+ return true;
+ bool validParameterValueEnd = (position == end) || isParameterValueEnd(*position);
+ return validParameterValueEnd && isExtensionParameter(name);
+}
+
+// Before:
+//
+// <cat.jpg>; rel="preload"; type="image/jpeg";
+// ^ ^
+// position end
+//
+// After (if the parameter starts with a quote, otherwise the method returns false)
+//
+// <cat.jpg>; rel="preload"; type="image/jpeg";
+// ^ ^
+// position end
+template <typename CharacterType>
+static bool skipQuotesIfNeeded(CharacterType*& position, CharacterType* const end, bool& completeQuotes)
+{
+ ASSERT(position <= end);
+ unsigned char quote;
+ if (skipExactly<CharacterType>(position, end, '\''))
+ quote = '\'';
+ else if (skipExactly<CharacterType>(position, end, '"'))
+ quote = '"';
+ else
+ return false;
+
+ while (!completeQuotes && position < end) {
+ skipUntil(position, end, static_cast<CharacterType>(quote));
+ if (*(position - 1) != '\\')
+ completeQuotes = true;
+ completeQuotes = skipExactly(position, end, static_cast<CharacterType>(quote)) && completeQuotes;
+ }
+ return true;
+}
+
+// Before:
+//
+// <cat.jpg>; rel=preload; foo=bar
+// ^ ^
+// position end
+//
+// After (if successful: otherwise the method returns false)
+//
+// <cat.jpg>; rel=preload; foo=bar
+// ^ ^
+// position end
+template <typename CharacterType>
+static bool parseParameterValue(CharacterType*& position, CharacterType* const end, String& value)
+{
+ ASSERT(position <= end);
+ CharacterType* valueStart = position;
+ CharacterType* valueEnd = position;
+ bool completeQuotes = false;
+ bool hasQuotes = skipQuotesIfNeeded(position, end, completeQuotes);
+ if (!hasQuotes)
+ skipWhile<CharacterType, isParameterValueChar>(position, end);
+ valueEnd = position;
+ skipWhile<CharacterType, isSpaceOrTab>(position, end);
+ if ((!completeQuotes && valueStart == valueEnd) || (position != end && !isParameterValueEnd(*position))) {
+ value = String("");
+ return false;
+ }
+ if (hasQuotes)
+ ++valueStart;
+ if (completeQuotes)
+ --valueEnd;
+ ASSERT(valueEnd >= valueStart);
+ value = String(valueStart, valueEnd - valueStart);
+ return !hasQuotes || completeQuotes;
+}
+
+void LinkHeader::setValue(LinkParameterName name, String value)
+{
+ switch (name) {
+ case LinkParameterRel:
+ if (!m_rel)
+ m_rel = value;
+ break;
+ case LinkParameterAnchor:
+ m_isValid = false;
+ break;
+ case LinkParameterCrossOrigin:
+ m_crossOrigin = value;
+ break;
+ case LinkParameterAs:
+ m_as = value;
+ break;
+ case LinkParameterType:
+ m_mimeType = value;
+ break;
+ case LinkParameterMedia:
+ m_media = value;
+ break;
+ case LinkParameterTitle:
+ case LinkParameterRev:
+ case LinkParameterHreflang:
+ case LinkParameterUnknown:
+ // These parameters are not yet supported, so they are currently ignored.
+ break;
+ }
+ // FIXME: Add support for more header parameters as neccessary.
+}
+
+template <typename CharacterType>
+static void findNextHeader(CharacterType*& position, CharacterType* const end)
+{
+ ASSERT(position <= end);
+ skipUntil<CharacterType>(position, end, ',');
+ skipExactly<CharacterType>(position, end, ',');
+}
+
+template <typename CharacterType>
+LinkHeader::LinkHeader(CharacterType*& position, CharacterType* const end)
+{
+ ASSERT(position <= end);
+ auto urlResult = findURLBoundaries(position, end);
+ if (urlResult == std::nullopt) {
+ m_isValid = false;
+ findNextHeader(position, end);
+ return;
+ }
+ m_url = urlResult.value();
+
+ while (m_isValid && position < end) {
+ if (!parseParameterDelimiter(position, end, m_isValid)) {
+ findNextHeader(position, end);
+ return;
+ }
+
+ LinkParameterName parameterName;
+ if (!parseParameterName(position, end, parameterName)) {
+ findNextHeader(position, end);
+ m_isValid = false;
+ return;
+ }
+
+ String parameterValue;
+ if (!parseParameterValue(position, end, parameterValue) && !isExtensionParameter(parameterName)) {
+ findNextHeader(position, end);
+ m_isValid = false;
+ return;
+ }
+
+ setValue(parameterName, parameterValue);
+ }
+ findNextHeader(position, end);
+}
+
+LinkHeaderSet::LinkHeaderSet(const String& header)
+{
+ if (header.isNull())
+ return;
+
+ if (header.is8Bit())
+ init(header.characters8(), header.length());
+ else
+ init(header.characters16(), header.length());
+}
+
+template <typename CharacterType>
+void LinkHeaderSet::init(CharacterType* headerValue, size_t length)
+{
+ CharacterType* position = headerValue;
+ CharacterType* const end = headerValue + length;
+ while (position < end)
+ m_headerSet.append(LinkHeader(position, end));
+}
+
+} // namespace WebCore
+