1/* 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 5 * Copyright (C) 2008 Alp Toker <alp@atoker.com> 6 * Copyright (C) Research In Motion Limited 2009. All rights reserved. 7 * Copyright (C) 2011 Kris Jordan <krisjordan@gmail.com> 8 * Copyright (C) 2011 Google Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 20 * its contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35#include "config.h" 36#include "IconController.h" 37 38#include "Document.h" 39#include "DocumentLoader.h" 40#include "Frame.h" 41#include "FrameLoader.h" 42#include "FrameLoaderClient.h" 43#include "IconDatabase.h" 44#include "IconDatabaseBase.h" 45#include "IconLoader.h" 46#include "IconURL.h" 47#include "Logging.h" 48#include "Page.h" 49#include "Settings.h" 50 51namespace WebCore { 52 53IconController::IconController(Frame* frame) 54 : m_frame(frame) 55 , m_waitingForLoadDecision(false) 56{ 57} 58 59IconController::~IconController() 60{ 61} 62 63KURL IconController::url() 64{ 65 IconURLs iconURLs = urlsForTypes(Favicon); 66 return iconURLs.isEmpty() ? KURL() : iconURLs[0].m_iconURL; 67} 68 69IconURL IconController::iconURL(IconType iconType) const 70{ 71 IconURL result; 72 const Vector<IconURL>& iconURLs = m_frame->document()->iconURLs(iconType); 73 Vector<IconURL>::const_iterator iter(iconURLs.begin()); 74 for (; iter != iconURLs.end(); ++iter) { 75 if (result.m_iconURL.isEmpty() || !iter->m_mimeType.isEmpty()) 76 result = *iter; 77 } 78 79 return result; 80} 81 82IconURLs IconController::urlsForTypes(int iconTypesMask) 83{ 84 IconURLs iconURLs; 85 if (m_frame->tree() && m_frame->tree()->parent()) 86 return iconURLs; 87 88 if (iconTypesMask & Favicon && !appendToIconURLs(Favicon, &iconURLs)) 89 iconURLs.append(defaultURL(Favicon)); 90 91#if ENABLE(TOUCH_ICON_LOADING) 92 int missedIcons = 0; 93 if (iconTypesMask & TouchPrecomposedIcon) 94 missedIcons += appendToIconURLs(TouchPrecomposedIcon, &iconURLs) ? 0:1; 95 96 if (iconTypesMask & TouchIcon) 97 missedIcons += appendToIconURLs(TouchIcon, &iconURLs) ? 0:1; 98 99 // Only return the default touch icons when the both were required and neither was gotten. 100 if (missedIcons == 2) { 101 iconURLs.append(defaultURL(TouchPrecomposedIcon)); 102 iconURLs.append(defaultURL(TouchIcon)); 103 } 104#endif 105 106 // Finally, append all remaining icons of this type. 107 const Vector<IconURL>& allIconURLs = m_frame->document()->iconURLs(iconTypesMask); 108 for (Vector<IconURL>::const_iterator iter = allIconURLs.begin(); iter != allIconURLs.end(); ++iter) { 109 int i; 110 int iconCount = iconURLs.size(); 111 for (i = 0; i < iconCount; ++i) { 112 if (*iter == iconURLs.at(i)) 113 break; 114 } 115 if (i == iconCount) 116 iconURLs.append(*iter); 117 } 118 119 return iconURLs; 120} 121 122void IconController::commitToDatabase(const KURL& icon) 123{ 124 LOG(IconDatabase, "Committing iconURL %s to database for pageURLs %s and %s", icon.string().ascii().data(), m_frame->document()->url().string().ascii().data(), m_frame->loader()->initialRequest().url().string().ascii().data()); 125 iconDatabase().setIconURLForPageURL(icon.string(), m_frame->document()->url().string()); 126 iconDatabase().setIconURLForPageURL(icon.string(), m_frame->loader()->initialRequest().url().string()); 127} 128 129void IconController::startLoader() 130{ 131 // FIXME: We kick off the icon loader when the frame is done receiving its main resource. 132 // But we should instead do it when we're done parsing the head element. 133 if (!m_frame->loader()->isLoadingMainFrame()) 134 return; 135 136 if (!iconDatabase().isEnabled()) 137 return; 138 139 ASSERT(!m_frame->tree()->parent()); 140 if (!documentCanHaveIcon(m_frame->document()->url())) 141 return; 142 143 KURL iconURL(url()); 144 String urlString(iconURL.string()); 145 if (urlString.isEmpty()) 146 return; 147 148 // People who want to avoid loading images generally want to avoid loading all images, unless an exception has been made for site icons. 149 // Now that we've accounted for URL mapping, avoid starting the network load if images aren't set to display automatically. 150 Settings* settings = m_frame->settings(); 151 if (settings && !settings->loadsImagesAutomatically() && !settings->loadsSiteIconsIgnoringImageLoadingSetting()) 152 return; 153 154 // If we're reloading the page, always start the icon load now. 155 // FIXME: How can this condition ever be true? 156 if (m_frame->loader()->loadType() == FrameLoadTypeReload && m_frame->loader()->loadType() == FrameLoadTypeReloadFromOrigin) { 157 continueLoadWithDecision(IconLoadYes); 158 return; 159 } 160 161 if (iconDatabase().supportsAsynchronousMode()) { 162 m_frame->loader()->documentLoader()->getIconLoadDecisionForIconURL(urlString); 163 // Commit the icon url mapping to the database just in case we don't end up loading later. 164 commitToDatabase(iconURL); 165 return; 166 } 167 168 IconLoadDecision decision = iconDatabase().synchronousLoadDecisionForIconURL(urlString, m_frame->loader()->documentLoader()); 169 170 if (decision == IconLoadUnknown) { 171 // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database 172 // just in case we don't end up loading later - if we commit the mapping a second time after the load, that's no big deal 173 // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the 174 // icon is later read in from disk 175 LOG(IconDatabase, "IconController %p might load icon %s later", this, urlString.ascii().data()); 176 m_waitingForLoadDecision = true; 177 m_frame->loader()->client()->registerForIconNotification(); 178 commitToDatabase(iconURL); 179 return; 180 } 181 182 continueLoadWithDecision(decision); 183} 184 185void IconController::stopLoader() 186{ 187 if (m_iconLoader) 188 m_iconLoader->stopLoading(); 189} 190 191// Callback for the old-style synchronous IconDatabase interface. 192void IconController::loadDecisionReceived(IconLoadDecision iconLoadDecision) 193{ 194 if (!m_waitingForLoadDecision) 195 return; 196 LOG(IconDatabase, "IconController %p was told a load decision is available for its icon", this); 197 continueLoadWithDecision(iconLoadDecision); 198 m_waitingForLoadDecision = false; 199} 200 201void IconController::continueLoadWithDecision(IconLoadDecision iconLoadDecision) 202{ 203 ASSERT(iconLoadDecision != IconLoadUnknown); 204 205 // FIXME (<rdar://problem/9168605>) - We should support in-memory-only private browsing icons in asynchronous icon database mode. 206 if (iconDatabase().supportsAsynchronousMode() && m_frame->page()->settings()->privateBrowsingEnabled()) 207 return; 208 209 if (iconLoadDecision == IconLoadNo) { 210 KURL iconURL(url()); 211 String urlString(iconURL.string()); 212 if (urlString.isEmpty()) 213 return; 214 215 LOG(IconDatabase, "IconController::startLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data()); 216 commitToDatabase(iconURL); 217 218 if (iconDatabase().supportsAsynchronousMode()) { 219 m_frame->loader()->documentLoader()->getIconDataForIconURL(urlString); 220 return; 221 } 222 223 // We were told not to load this icon - that means this icon is already known by the database 224 // If the icon data hasn't been read in from disk yet, kick off the read of the icon from the database to make sure someone 225 // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method. 226 // Otherwise if the icon data *is* available, notify the delegate 227 if (!iconDatabase().synchronousIconDataKnownForIconURL(urlString)) { 228 LOG(IconDatabase, "Told not to load icon %s but icon data is not yet available - registering for notification and requesting load from disk", urlString.ascii().data()); 229 m_frame->loader()->client()->registerForIconNotification(); 230 iconDatabase().synchronousIconForPageURL(m_frame->document()->url().string(), IntSize(0, 0)); 231 iconDatabase().synchronousIconForPageURL(m_frame->loader()->initialRequest().url().string(), IntSize(0, 0)); 232 } else 233 m_frame->loader()->client()->dispatchDidReceiveIcon(); 234 235 return; 236 } 237 238 if (!m_iconLoader) 239 m_iconLoader = IconLoader::create(m_frame); 240 241 m_iconLoader->startLoading(); 242} 243 244bool IconController::appendToIconURLs(IconType iconType, IconURLs* iconURLs) 245{ 246 IconURL faviconURL = iconURL(iconType); 247 if (faviconURL.m_iconURL.isEmpty()) 248 return false; 249 250 iconURLs->append(faviconURL); 251 return true; 252} 253 254IconURL IconController::defaultURL(IconType iconType) 255{ 256 // Don't return a favicon iconURL unless we're http or https 257 KURL documentURL = m_frame->document()->url(); 258 if (!documentURL.protocolIsInHTTPFamily()) 259 return IconURL(); 260 261 KURL url; 262 bool couldSetProtocol = url.setProtocol(documentURL.protocol()); 263 ASSERT_UNUSED(couldSetProtocol, couldSetProtocol); 264 url.setHost(documentURL.host()); 265 if (documentURL.hasPort()) 266 url.setPort(documentURL.port()); 267 268 if (iconType == Favicon) { 269 url.setPath("/favicon.ico"); 270 return IconURL::defaultIconURL(url, Favicon); 271 } 272#if ENABLE(TOUCH_ICON_LOADING) 273 if (iconType == TouchPrecomposedIcon) { 274 url.setPath("/apple-touch-icon-precomposed.png"); 275 return IconURL::defaultIconURL(url, TouchPrecomposedIcon); 276 } 277 if (iconType == TouchIcon) { 278 url.setPath("/apple-touch-icon.png"); 279 return IconURL::defaultIconURL(url, TouchIcon); 280 } 281#endif 282 return IconURL(); 283} 284 285} 286