WebCore/loader/icon/IconFetcher.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/loader/icon/IconFetcher.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "IconFetcher.h"
+
+#include "Frame.h"
+#include "HTMLHeadElement.h"
+#include "HTMLLinkElement.h"
+#include "HTMLNames.h"
+#include "ResourceHandle.h"
+#include "ResourceRequest.h"
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+struct IconLinkEntry {
+public:
+    enum IconType {
+        Unknown,
+        ICNS,
+        ICO,
+    };
+    
+    IconLinkEntry(IconType type, const KURL& url) 
+        : m_type(type)
+        , m_url(url)
+    {
+    }
+    
+    IconType type() const { return m_type; }
+    const KURL& url() const { return m_url; }
+
+    SharedBuffer* buffer() 
+    {
+        if (!m_buffer)
+            m_buffer = SharedBuffer::create();
+        
+        return m_buffer.get();
+    }
+    
+private:
+    RefPtr<SharedBuffer> m_buffer;
+    IconType m_type;
+    KURL m_url;
+};
+    
+#if PLATFORM(MAC)
+static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICNS;
+#elif PLATFORM(WIN)
+static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICO;
+#else
+static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::Unknown;
+#endif
+
+static void parseIconLink(HTMLLinkElement* link, Vector<IconLinkEntry>& entries)
+{
+    // FIXME: Parse the size attribute too.
+    
+    IconLinkEntry::IconType type = IconLinkEntry::Unknown;
+    const KURL& url = link->href();
+
+    // Try to determine the file type.
+    String path = url.path();
+    
+    int pos = path.reverseFind('.');
+    if (pos >= 0) {
+        String extension = path.substring(pos + 1);
+        if (equalIgnoringCase(extension, "icns"))
+            type = IconLinkEntry::ICNS;
+        else if (equalIgnoringCase(extension, "ico"))
+            type = IconLinkEntry::ICO;
+    }
+    
+    entries.append(IconLinkEntry(type, url));
+}
+    
+PassRefPtr<IconFetcher> IconFetcher::create(Frame* frame, IconFetcherClient* client)
+{
+    Document* document = frame->document();
+    
+    HTMLHeadElement* head = document->head();
+    if (!head)
+        return 0;
+    
+    Vector<IconLinkEntry> entries;
+    
+    for (Node* n = head; n; n = n->traverseNextNode()) {
+        if (!n->hasTagName(linkTag))    
+            continue;
+            
+        HTMLLinkElement* link = static_cast<HTMLLinkElement*>(n);
+        if (!link->isIcon())
+            continue;
+
+        parseIconLink(link, entries);
+    }
+    
+    if (entries.isEmpty())
+        return 0;
+    
+    // Check if any of the entries have the same type as the native icon type.
+
+    // FIXME: This should be way more sophisticated, and handle conversion
+    // of multisize formats for example.
+    for (unsigned i = 0; i < entries.size(); i++) {
+        const IconLinkEntry& entry = entries[i];
+        if (entry.type() == NativeIconType) {
+            RefPtr<IconFetcher> iconFetcher = adoptRef(new IconFetcher(frame, client));
+            
+            iconFetcher->m_entries.append(entry);
+            iconFetcher->loadEntry();
+            
+            return iconFetcher.release();
+        }
+    }
+
+    return 0;
+}    
+
+IconFetcher::IconFetcher(Frame* frame, IconFetcherClient* client)
+    : m_frame(frame)
+    , m_client(client)
+    , m_currentEntry(0)
+{
+}
+    
+IconFetcher::~IconFetcher()
+{
+    cancel();
+}
+
+void IconFetcher::cancel()
+{
+    if (m_handle)
+        m_handle->cancel();
+}
+
+PassRefPtr<SharedBuffer> IconFetcher::createIcon()
+{
+    ASSERT(!m_entries.isEmpty());
+    
+    // For now, just return the data of the first entry.
+    return m_entries.first().buffer();
+}
+
+void IconFetcher::loadEntry()
+{
+    ASSERT(m_currentEntry < m_entries.size());
+    ASSERT(!m_handle);
+    
+    m_handle = ResourceHandle::create(m_entries[m_currentEntry].url(), this, m_frame, false, false);
+}
+    
+void IconFetcher::loadFailed()
+{
+    m_handle = 0;
+    
+    m_client->finishedFetchingIcon(0);
+}    
+    
+void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
+{
+    ASSERT_UNUSED(handle, m_handle == handle);
+    
+    int statusCode = response.httpStatusCode() / 100;
+    if (statusCode == 4 || statusCode == 5) {
+        loadFailed();
+        return;
+    }    
+}
+    
+void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int)
+{
+    ASSERT_UNUSED(handle, m_handle == handle);
+    
+    m_entries[m_currentEntry].buffer()->append(data, length);
+}
+
+void IconFetcher::didFinishLoading(ResourceHandle* handle)
+{
+    ASSERT_UNUSED(handle, m_handle == handle);
+    
+    if (m_currentEntry == m_entries.size() - 1) {
+        // We finished loading, create the icon
+        RefPtr<SharedBuffer> iconData = createIcon();
+        
+        m_client->finishedFetchingIcon(iconData.release());
+        return;
+    }
+    
+    // Load the next entry
+    m_currentEntry++;
+
+    loadEntry();
+}
+    
+void IconFetcher::didFail(ResourceHandle* handle, const ResourceError&)
+{
+    ASSERT_UNUSED(handle, m_handle == handle);
+    
+    loadFailed();
+}
+
+} // namespace WebCore