1 /*
2 * Copyright (C) 2008 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "IconFetcher.h"
28
29 #include "Frame.h"
30 #include "HTMLHeadElement.h"
31 #include "HTMLLinkElement.h"
32 #include "HTMLNames.h"
33 #include "ResourceHandle.h"
34 #include "ResourceRequest.h"
35
36 namespace WebCore {
37
38 using namespace HTMLNames;
39
40 struct IconLinkEntry {
41 public:
42 enum IconType {
43 Unknown,
44 ICNS,
45 ICO,
46 };
47
IconLinkEntryWebCore::IconLinkEntry48 IconLinkEntry(IconType type, const KURL& url)
49 : m_type(type)
50 , m_url(url)
51 {
52 }
53
typeWebCore::IconLinkEntry54 IconType type() const { return m_type; }
urlWebCore::IconLinkEntry55 const KURL& url() const { return m_url; }
56
bufferWebCore::IconLinkEntry57 SharedBuffer* buffer()
58 {
59 if (!m_buffer)
60 m_buffer = SharedBuffer::create();
61
62 return m_buffer.get();
63 }
64
65 private:
66 RefPtr<SharedBuffer> m_buffer;
67 IconType m_type;
68 KURL m_url;
69 };
70
71 #if PLATFORM(MAC)
72 static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICNS;
73 #elif PLATFORM(WIN)
74 static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICO;
75 #else
76 static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::Unknown;
77 #endif
78
parseIconLink(HTMLLinkElement * link,Vector<IconLinkEntry> & entries)79 static void parseIconLink(HTMLLinkElement* link, Vector<IconLinkEntry>& entries)
80 {
81 // FIXME: Parse the size attribute too.
82
83 IconLinkEntry::IconType type = IconLinkEntry::Unknown;
84 const KURL& url = link->href();
85
86 // Try to determine the file type.
87 String path = url.path();
88
89 int pos = path.reverseFind('.');
90 if (pos >= 0) {
91 String extension = path.substring(pos + 1);
92 if (equalIgnoringCase(extension, "icns"))
93 type = IconLinkEntry::ICNS;
94 else if (equalIgnoringCase(extension, "ico"))
95 type = IconLinkEntry::ICO;
96 }
97
98 entries.append(IconLinkEntry(type, url));
99 }
100
create(Frame * frame,IconFetcherClient * client)101 PassRefPtr<IconFetcher> IconFetcher::create(Frame* frame, IconFetcherClient* client)
102 {
103 Document* document = frame->document();
104 if (!document)
105 return 0;
106
107 HTMLHeadElement* head = document->head();
108 if (!head)
109 return 0;
110
111 Vector<IconLinkEntry> entries;
112
113 for (Node* n = head; n; n = n->traverseNextNode()) {
114 if (!n->hasTagName(linkTag))
115 continue;
116
117 HTMLLinkElement* link = static_cast<HTMLLinkElement*>(n);
118 if (!link->isIcon())
119 continue;
120
121 parseIconLink(link, entries);
122 }
123
124 if (entries.isEmpty())
125 return 0;
126
127 // Check if any of the entries have the same type as the native icon type.
128
129 // FIXME: This should be way more sophisticated, and handle conversion
130 // of multisize formats for example.
131 for (unsigned i = 0; i < entries.size(); i++) {
132 const IconLinkEntry& entry = entries[i];
133 if (entry.type() == NativeIconType) {
134 RefPtr<IconFetcher> iconFetcher = adoptRef(new IconFetcher(frame, client));
135
136 iconFetcher->m_entries.append(entry);
137 iconFetcher->loadEntry();
138
139 return iconFetcher.release();
140 }
141 }
142
143 return 0;
144 }
145
IconFetcher(Frame * frame,IconFetcherClient * client)146 IconFetcher::IconFetcher(Frame* frame, IconFetcherClient* client)
147 : m_frame(frame)
148 , m_client(client)
149 , m_currentEntry(0)
150 {
151 }
152
~IconFetcher()153 IconFetcher::~IconFetcher()
154 {
155 cancel();
156 }
157
cancel()158 void IconFetcher::cancel()
159 {
160 if (m_handle)
161 m_handle->cancel();
162 }
163
createIcon()164 PassRefPtr<SharedBuffer> IconFetcher::createIcon()
165 {
166 ASSERT(!m_entries.isEmpty());
167
168 // For now, just return the data of the first entry.
169 return m_entries.first().buffer();
170 }
171
loadEntry()172 void IconFetcher::loadEntry()
173 {
174 ASSERT(m_currentEntry < m_entries.size());
175 ASSERT(!m_handle);
176
177 m_handle = ResourceHandle::create(m_entries[m_currentEntry].url(), this, m_frame, false, false);
178 }
179
loadFailed()180 void IconFetcher::loadFailed()
181 {
182 m_handle = 0;
183
184 m_client->finishedFetchingIcon(0);
185 }
186
didReceiveResponse(ResourceHandle * handle,const ResourceResponse & response)187 void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
188 {
189 ASSERT_UNUSED(handle, m_handle == handle);
190
191 int statusCode = response.httpStatusCode() / 100;
192 if (statusCode == 4 || statusCode == 5) {
193 loadFailed();
194 return;
195 }
196 }
197
didReceiveData(ResourceHandle * handle,const char * data,int length,int)198 void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int)
199 {
200 ASSERT_UNUSED(handle, m_handle == handle);
201
202 m_entries[m_currentEntry].buffer()->append(data, length);
203 }
204
didFinishLoading(ResourceHandle * handle)205 void IconFetcher::didFinishLoading(ResourceHandle* handle)
206 {
207 ASSERT_UNUSED(handle, m_handle == handle);
208
209 if (m_currentEntry == m_entries.size() - 1) {
210 // We finished loading, create the icon
211 RefPtr<SharedBuffer> iconData = createIcon();
212
213 m_client->finishedFetchingIcon(iconData.release());
214 return;
215 }
216
217 // Load the next entry
218 m_currentEntry++;
219
220 loadEntry();
221 }
222
didFail(ResourceHandle * handle,const ResourceError &)223 void IconFetcher::didFail(ResourceHandle* handle, const ResourceError&)
224 {
225 ASSERT_UNUSED(handle, m_handle == handle);
226
227 loadFailed();
228 }
229
230 } // namespace WebCore
231