Home | History | Annotate | Download | only in icon
      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