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 48 IconLinkEntry(IconType type, const KURL& url) 49 : m_type(type) 50 , m_url(url) 51 { 52 } 53 54 IconType type() const { return m_type; } 55 const KURL& url() const { return m_url; } 56 57 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 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 101 PassRefPtr<IconFetcher> IconFetcher::create(Frame* frame, IconFetcherClient* client) 102 { 103 Document* document = frame->document(); 104 105 HTMLHeadElement* head = document->head(); 106 if (!head) 107 return 0; 108 109 Vector<IconLinkEntry> entries; 110 111 for (Node* n = head; n; n = n->traverseNextNode()) { 112 if (!n->hasTagName(linkTag)) 113 continue; 114 115 HTMLLinkElement* link = static_cast<HTMLLinkElement*>(n); 116 if (!link->isIcon()) 117 continue; 118 119 parseIconLink(link, entries); 120 } 121 122 if (entries.isEmpty()) 123 return 0; 124 125 // Check if any of the entries have the same type as the native icon type. 126 127 // FIXME: This should be way more sophisticated, and handle conversion 128 // of multisize formats for example. 129 for (unsigned i = 0; i < entries.size(); i++) { 130 const IconLinkEntry& entry = entries[i]; 131 if (entry.type() == NativeIconType) { 132 RefPtr<IconFetcher> iconFetcher = adoptRef(new IconFetcher(frame, client)); 133 134 iconFetcher->m_entries.append(entry); 135 iconFetcher->loadEntry(); 136 137 return iconFetcher.release(); 138 } 139 } 140 141 return 0; 142 } 143 144 IconFetcher::IconFetcher(Frame* frame, IconFetcherClient* client) 145 : m_frame(frame) 146 , m_client(client) 147 , m_currentEntry(0) 148 { 149 } 150 151 IconFetcher::~IconFetcher() 152 { 153 cancel(); 154 } 155 156 void IconFetcher::cancel() 157 { 158 if (m_handle) 159 m_handle->cancel(); 160 } 161 162 PassRefPtr<SharedBuffer> IconFetcher::createIcon() 163 { 164 ASSERT(!m_entries.isEmpty()); 165 166 // For now, just return the data of the first entry. 167 return m_entries.first().buffer(); 168 } 169 170 void IconFetcher::loadEntry() 171 { 172 ASSERT(m_currentEntry < m_entries.size()); 173 ASSERT(!m_handle); 174 175 m_handle = ResourceHandle::create(m_entries[m_currentEntry].url(), this, m_frame, false, false); 176 } 177 178 void IconFetcher::loadFailed() 179 { 180 m_handle = 0; 181 182 m_client->finishedFetchingIcon(0); 183 } 184 185 void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) 186 { 187 ASSERT_UNUSED(handle, m_handle == handle); 188 189 int statusCode = response.httpStatusCode() / 100; 190 if (statusCode == 4 || statusCode == 5) { 191 loadFailed(); 192 return; 193 } 194 } 195 196 void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int) 197 { 198 ASSERT_UNUSED(handle, m_handle == handle); 199 200 m_entries[m_currentEntry].buffer()->append(data, length); 201 } 202 203 void IconFetcher::didFinishLoading(ResourceHandle* handle) 204 { 205 ASSERT_UNUSED(handle, m_handle == handle); 206 207 if (m_currentEntry == m_entries.size() - 1) { 208 // We finished loading, create the icon 209 RefPtr<SharedBuffer> iconData = createIcon(); 210 211 m_client->finishedFetchingIcon(iconData.release()); 212 return; 213 } 214 215 // Load the next entry 216 m_currentEntry++; 217 218 loadEntry(); 219 } 220 221 void IconFetcher::didFail(ResourceHandle* handle, const ResourceError&) 222 { 223 ASSERT_UNUSED(handle, m_handle == handle); 224 225 loadFailed(); 226 } 227 228 } // namespace WebCore 229