#include "config.h"
#include "URL.h"
#include "LinkHash.h"
#include <wtf/text/AtomicString.h>
#include <wtf/text/StringHash.h>
#include <wtf/text/StringView.h>
namespace WebCore {
template <typename CharacterType>
static inline size_t findSlashDotDotSlash(const CharacterType* characters, size_t length, size_t position)
{
if (length < 4)
return notFound;
size_t loopLimit = length - 3;
for (size_t i = position; i < loopLimit; ++i) {
if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2] == '.' && characters[i + 3] == '/')
return i;
}
return notFound;
}
template <typename CharacterType>
static inline size_t findSlashSlash(const CharacterType* characters, size_t length, size_t position)
{
if (length < 2)
return notFound;
size_t loopLimit = length - 1;
for (size_t i = position; i < loopLimit; ++i) {
if (characters[i] == '/' && characters[i + 1] == '/')
return i;
}
return notFound;
}
template <typename CharacterType>
static inline size_t findSlashDotSlash(const CharacterType* characters, size_t length, size_t position)
{
if (length < 3)
return notFound;
size_t loopLimit = length - 2;
for (size_t i = position; i < loopLimit; ++i) {
if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2] == '/')
return i;
}
return notFound;
}
template <typename CharacterType>
static inline bool containsColonSlashSlash(const CharacterType* characters, unsigned length)
{
if (length < 3)
return false;
unsigned loopLimit = length - 2;
for (unsigned i = 0; i < loopLimit; ++i) {
if (characters[i] == ':' && characters[i + 1] == '/' && characters[i + 2] == '/')
return true;
}
return false;
}
template <typename CharacterType>
static inline void squeezeOutNullCharacters(Vector<CharacterType, 512>& string)
{
size_t size = string.size();
size_t i = 0;
for (i = 0; i < size; ++i) {
if (!string[i])
break;
}
if (i == size)
return;
size_t j = i;
for (++i; i < size; ++i) {
if (CharacterType character = string[i])
string[j++] = character;
}
ASSERT(j < size);
string.shrink(j);
}
template <typename CharacterType>
static void cleanSlashDotDotSlashes(Vector<CharacterType, 512>& path, size_t firstSlash)
{
size_t slash = firstSlash;
do {
size_t previousSlash = slash ? reverseFind(path.data(), path.size(), '/', slash - 1) : notFound;
if (previousSlash == notFound || (previousSlash > 3 && path[previousSlash - 2] == ':' && path[previousSlash - 1] == '/')) {
path[slash] = 0;
path[slash + 1] = 0;
path[slash + 2] = 0;
} else {
for (size_t i = previousSlash; i < slash + 3; ++i)
path[i] = 0;
}
slash += 3;
} while ((slash = findSlashDotDotSlash(path.data(), path.size(), slash)) != notFound);
squeezeOutNullCharacters(path);
}
template <typename CharacterType>
static void mergeDoubleSlashes(Vector<CharacterType, 512>& path, size_t firstSlash)
{
size_t refPos = find(path.data(), path.size(), '#');
if (!refPos || refPos == notFound)
refPos = path.size();
size_t slash = firstSlash;
while (slash < refPos) {
if (!slash || path[slash - 1] != ':')
path[slash++] = 0;
else
slash += 2;
if ((slash = findSlashSlash(path.data(), path.size(), slash)) == notFound)
break;
}
squeezeOutNullCharacters(path);
}
template <typename CharacterType>
static void cleanSlashDotSlashes(Vector<CharacterType, 512>& path, size_t firstSlash)
{
size_t slash = firstSlash;
do {
path[slash] = 0;
path[slash + 1] = 0;
slash += 2;
} while ((slash = findSlashDotSlash(path.data(), path.size(), slash)) != notFound);
squeezeOutNullCharacters(path);
}
template <typename CharacterType>
static inline void cleanPath(Vector<CharacterType, 512>& path)
{
size_t firstSlash = findSlashDotDotSlash(path.data(), path.size(), 0);
if (firstSlash != notFound)
cleanSlashDotDotSlashes(path, firstSlash);
firstSlash = findSlashSlash(path.data(), path.size(), 0);
if (firstSlash != notFound)
mergeDoubleSlashes(path, firstSlash);
firstSlash = findSlashDotSlash(path.data(), path.size(), 0);
if (firstSlash != notFound)
cleanSlashDotSlashes(path, firstSlash);
}
template <typename CharacterType>
static inline bool matchLetter(CharacterType c, char lowercaseLetter)
{
return (c | 0x20) == lowercaseLetter;
}
template <typename CharacterType>
static inline bool needsTrailingSlash(const CharacterType* characters, unsigned length)
{
if (length < 6)
return false;
if (!matchLetter(characters[0], 'h')
|| !matchLetter(characters[1], 't')
|| !matchLetter(characters[2], 't')
|| !matchLetter(characters[3], 'p'))
return false;
if (!(characters[4] == ':'
|| (matchLetter(characters[4], 's') && characters[5] == ':')))
return false;
unsigned pos = characters[4] == ':' ? 5 : 6;
if (pos + 1 < length && characters[pos] == '/' && characters[pos + 1] == '/')
pos += 2;
while (pos < length && characters[pos] != '/')
++pos;
return pos == length;
}
template <typename CharacterType>
static ALWAYS_INLINE LinkHash visitedLinkHashInline(const CharacterType* url, unsigned length)
{
return AlreadyHashed::avoidDeletedValue(StringHasher::computeHash(url, length));
}
LinkHash visitedLinkHash(const String& url)
{
unsigned length = url.length();
if (!length || url.is8Bit())
return visitedLinkHashInline(url.characters8(), length);
return visitedLinkHashInline(url.characters16(), length);
}
LinkHash visitedLinkHash(const UChar* url, unsigned length)
{
return visitedLinkHashInline(url, length);
}
template <typename CharacterType>
static ALWAYS_INLINE void visitedURLInline(const URL& base, const CharacterType* characters, unsigned length, Vector<CharacterType, 512>& buffer)
{
if (!length)
return;
bool hasColonSlashSlash = containsColonSlashSlash(characters, length);
if (hasColonSlashSlash && !needsTrailingSlash(characters, length)) {
buffer.append(characters, length);
return;
}
if (hasColonSlashSlash) {
buffer.append(characters, length);
buffer.append('/');
return;
}
if (!length)
append(buffer, base.string());
else {
switch (characters[0]) {
case '/':
append(buffer, StringView(base.string()).substring(0, base.pathStart()));
break;
case '#':
append(buffer, StringView(base.string()).substring(0, base.pathEnd()));
break;
default:
append(buffer, StringView(base.string()).substring(0, base.pathAfterLastSlash()));
break;
}
}
buffer.append(characters, length);
cleanPath(buffer);
if (needsTrailingSlash(buffer.data(), buffer.size())) {
buffer.append('/');
}
return;
}
LinkHash visitedLinkHash(const URL& base, const AtomicString& attributeURL)
{
if (attributeURL.isEmpty())
return 0;
if (!base.string().isEmpty() && base.string().is8Bit() && attributeURL.is8Bit()) {
Vector<LChar, 512> url;
visitedURLInline(base, attributeURL.characters8(), attributeURL.length(), url);
if (url.isEmpty())
return 0;
return visitedLinkHashInline(url.data(), url.size());
}
Vector<UChar, 512> url;
auto upconvertedCharacters = StringView(attributeURL.string()).upconvertedCharacters();
const UChar* characters = upconvertedCharacters;
visitedURLInline(base, characters, attributeURL.length(), url);
if (url.isEmpty())
return 0;
return visitedLinkHashInline(url.data(), url.size());
}
}