1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "LinkIconCollector.h"
28
29#include "Document.h"
30#include "ElementChildIterator.h"
31#include "HTMLHeadElement.h"
32#include "HTMLLinkElement.h"
33#include "LinkIconType.h"
34
35namespace WebCore {
36
37const unsigned defaultTouchIconWidth = 60;
38
39static unsigned iconSize(const LinkIcon& icon)
40{
41 if (icon.size)
42 return *icon.size;
43
44 if (icon.type == LinkIconType::TouchIcon || icon.type == LinkIconType::TouchPrecomposedIcon)
45 return defaultTouchIconWidth;
46
47 return 0;
48}
49
50static int compareIcons(const LinkIcon& a, const LinkIcon& b)
51{
52 // Apple Touch icons always come first.
53 if (a.type == LinkIconType::Favicon && b.type != LinkIconType::Favicon)
54 return 1;
55 if (a.type == LinkIconType::Favicon && b.type != LinkIconType::Favicon)
56 return -1;
57
58 unsigned aSize = iconSize(a);
59 unsigned bSize = iconSize(b);
60
61 if (bSize > aSize)
62 return 1;
63 if (bSize < aSize)
64 return -1;
65
66 // A Precomposed icon should come first if both icons have the same size.
67 if (a.type != LinkIconType::TouchPrecomposedIcon && b.type == LinkIconType::TouchPrecomposedIcon)
68 return 1;
69 if (b.type != LinkIconType::TouchPrecomposedIcon && a.type == LinkIconType::TouchPrecomposedIcon)
70 return -1;
71
72 return 0;
73}
74
75auto LinkIconCollector::iconsOfTypes(OptionSet<LinkIconType> iconTypes) -> Vector<LinkIcon>
76{
77 auto head = makeRefPtr(m_document.head());
78 if (!head)
79 return { };
80
81 Vector<LinkIcon> icons;
82
83 for (auto& linkElement : childrenOfType<HTMLLinkElement>(*head)) {
84 if (!linkElement.iconType())
85 continue;
86
87 auto iconType = *linkElement.iconType();
88 if (!iconTypes.contains(iconType))
89 continue;
90
91 auto url = linkElement.href();
92 if (!url.protocolIsInHTTPFamily())
93 continue;
94
95 // This icon size parsing is a little wonky - it only parses the first
96 // part of the size, "60x70" becomes "60". This is for compatibility reasons
97 // and is probably good enough for now.
98 Optional<unsigned> iconSize;
99
100 if (linkElement.sizes().length()) {
101 bool ok;
102 unsigned size = linkElement.sizes().item(0).string().stripWhiteSpace().toUInt(&ok);
103 if (ok)
104 iconSize = size;
105 }
106
107 Vector<std::pair<String, String>> attributes;
108 if (linkElement.hasAttributes()) {
109 attributes.reserveCapacity(linkElement.attributeCount());
110 for (const Attribute& attribute : linkElement.attributesIterator())
111 attributes.uncheckedAppend({ attribute.localName(), attribute.value() });
112 }
113
114 icons.append({ url, iconType, linkElement.type(), iconSize, WTFMove(attributes) });
115 }
116
117 std::sort(icons.begin(), icons.end(), [](auto& a, auto& b) {
118 return compareIcons(a, b) < 0;
119 });
120
121 return icons;
122}
123
124}
125