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 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 "FrameLoader.h" 41#include "FrameLoaderClient.h" 42#include "IconDatabase.h" 43#include "IconDatabaseBase.h" 44#include "IconLoader.h" 45#include "IconURL.h" 46#include "Logging.h" 47#include "MainFrame.h" 48#include "Page.h" 49#include "Settings.h" 50#include <wtf/text/CString.h> 51 52namespace WebCore { 53 54IconController::IconController(Frame& frame) 55 : m_frame(frame) 56 , m_waitingForLoadDecision(false) 57{ 58} 59 60IconController::~IconController() 61{ 62} 63 64URL IconController::url() 65{ 66 IconURLs iconURLs = urlsForTypes(Favicon); 67 return iconURLs.isEmpty() ? URL() : iconURLs[0].m_iconURL; 68} 69 70IconURL IconController::iconURL(IconType iconType) const 71{ 72 IconURL result; 73 const Vector<IconURL>& iconURLs = m_frame.document()->iconURLs(iconType); 74 Vector<IconURL>::const_iterator iter(iconURLs.begin()); 75 for (; iter != iconURLs.end(); ++iter) { 76 if (result.m_iconURL.isEmpty() || !iter->m_mimeType.isEmpty()) 77 result = *iter; 78 } 79 80 return result; 81} 82 83IconURLs IconController::urlsForTypes(int iconTypesMask) 84{ 85 IconURLs iconURLs; 86 if (m_frame.tree().parent()) 87 return iconURLs; 88 89 if (iconTypesMask & Favicon && !appendToIconURLs(Favicon, &iconURLs)) 90 iconURLs.append(defaultURL(Favicon)); 91 92#if ENABLE(TOUCH_ICON_LOADING) 93 int missedIcons = 0; 94 if (iconTypesMask & TouchPrecomposedIcon) 95 missedIcons += appendToIconURLs(TouchPrecomposedIcon, &iconURLs) ? 0:1; 96 97 if (iconTypesMask & TouchIcon) 98 missedIcons += appendToIconURLs(TouchIcon, &iconURLs) ? 0:1; 99 100 // Only return the default touch icons when the both were required and neither was gotten. 101 if (missedIcons == 2) { 102 iconURLs.append(defaultURL(TouchPrecomposedIcon)); 103 iconURLs.append(defaultURL(TouchIcon)); 104 } 105#endif 106 107 // Finally, append all remaining icons of this type. 108 const Vector<IconURL>& allIconURLs = m_frame.document()->iconURLs(iconTypesMask); 109 for (Vector<IconURL>::const_iterator iter = allIconURLs.begin(); iter != allIconURLs.end(); ++iter) { 110 int i; 111 int iconCount = iconURLs.size(); 112 for (i = 0; i < iconCount; ++i) { 113 if (*iter == iconURLs.at(i)) 114 break; 115 } 116 if (i == iconCount) 117 iconURLs.append(*iter); 118 } 119 120 return iconURLs; 121} 122 123void IconController::commitToDatabase(const URL& icon) 124{ 125 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()); 126 iconDatabase().setIconURLForPageURL(icon.string(), m_frame.document()->url().string()); 127 iconDatabase().setIconURLForPageURL(icon.string(), m_frame.loader().initialRequest().url().string()); 128} 129 130void IconController::startLoader() 131{ 132 // FIXME: We kick off the icon loader when the frame is done receiving its main resource. 133 // But we should instead do it when we're done parsing the head element. 134 135 if (!m_frame.isMainFrame()) 136 return; 137 138 if (!iconDatabase().isEnabled()) 139 return; 140 141 ASSERT(!m_frame.tree().parent()); 142 if (!documentCanHaveIcon(m_frame.document()->url())) 143 return; 144 145 URL iconURL(url()); 146 String urlString(iconURL.string()); 147 if (urlString.isEmpty()) 148 return; 149 150 // People who want to avoid loading images generally want to avoid loading all images, unless an exception has been made for site icons. 151 // Now that we've accounted for URL mapping, avoid starting the network load if images aren't set to display automatically. 152 if (!m_frame.settings().loadsImagesAutomatically() && !m_frame.settings().loadsSiteIconsIgnoringImageLoadingSetting()) 153 return; 154 155 // If we're reloading the page, always start the icon load now. 156 // FIXME: How can this condition ever be true? 157 if (m_frame.loader().loadType() == FrameLoadType::Reload && m_frame.loader().loadType() == FrameLoadType::ReloadFromOrigin) { 158 continueLoadWithDecision(IconLoadYes); 159 return; 160 } 161 162 if (iconDatabase().supportsAsynchronousMode()) { 163 m_frame.loader().documentLoader()->getIconLoadDecisionForIconURL(urlString); 164 // Commit the icon url mapping to the database just in case we don't end up loading later. 165 commitToDatabase(iconURL); 166 return; 167 } 168 169 IconLoadDecision decision = iconDatabase().synchronousLoadDecisionForIconURL(urlString, m_frame.loader().documentLoader()); 170 171 if (decision == IconLoadUnknown) { 172 // In this case, we may end up loading the icon later, but we still want to commit the icon url mapping to the database 173 // 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 174 // We also tell the client to register for the notification that the icon is received now so it isn't missed in case the 175 // icon is later read in from disk 176 LOG(IconDatabase, "IconController %p might load icon %s later", this, urlString.ascii().data()); 177 m_waitingForLoadDecision = true; 178 m_frame.loader().client().registerForIconNotification(); 179 commitToDatabase(iconURL); 180 return; 181 } 182 183 continueLoadWithDecision(decision); 184} 185 186void IconController::stopLoader() 187{ 188 if (m_iconLoader) 189 m_iconLoader->stopLoading(); 190} 191 192// Callback for the old-style synchronous IconDatabase interface. 193void IconController::loadDecisionReceived(IconLoadDecision iconLoadDecision) 194{ 195 if (!m_waitingForLoadDecision) 196 return; 197 LOG(IconDatabase, "IconController %p was told a load decision is available for its icon", this); 198 continueLoadWithDecision(iconLoadDecision); 199 m_waitingForLoadDecision = false; 200} 201 202void IconController::continueLoadWithDecision(IconLoadDecision iconLoadDecision) 203{ 204 ASSERT(iconLoadDecision != IconLoadUnknown); 205 206 // FIXME (<rdar://problem/9168605>) - We should support in-memory-only private browsing icons in asynchronous icon database mode. 207 if (iconDatabase().supportsAsynchronousMode() && m_frame.page()->usesEphemeralSession()) 208 return; 209 210 if (iconLoadDecision == IconLoadNo) { 211 URL iconURL(url()); 212 String urlString(iconURL.string()); 213 if (urlString.isEmpty()) 214 return; 215 216 LOG(IconDatabase, "IconController::startLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data()); 217 commitToDatabase(iconURL); 218 219 if (iconDatabase().supportsAsynchronousMode()) { 220 m_frame.loader().documentLoader()->getIconDataForIconURL(urlString); 221 return; 222 } 223 224 // We were told not to load this icon - that means this icon is already known by the database 225 // 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 226 // has done it. This is after registering for the notification so the WebView can call the appropriate delegate method. 227 // Otherwise if the icon data *is* available, notify the delegate 228 if (!iconDatabase().synchronousIconDataKnownForIconURL(urlString)) { 229 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()); 230 m_frame.loader().client().registerForIconNotification(); 231 iconDatabase().synchronousIconForPageURL(m_frame.document()->url().string(), IntSize(0, 0)); 232 iconDatabase().synchronousIconForPageURL(m_frame.loader().initialRequest().url().string(), IntSize(0, 0)); 233 } else 234 m_frame.loader().client().dispatchDidReceiveIcon(); 235 236 return; 237 } 238 239 if (!m_iconLoader) 240 m_iconLoader = std::make_unique<IconLoader>(m_frame); 241 242 m_iconLoader->startLoading(); 243} 244 245bool IconController::appendToIconURLs(IconType iconType, IconURLs* iconURLs) 246{ 247 IconURL faviconURL = iconURL(iconType); 248 if (faviconURL.m_iconURL.isEmpty()) 249 return false; 250 251 iconURLs->append(faviconURL); 252 return true; 253} 254 255IconURL IconController::defaultURL(IconType iconType) 256{ 257 // Don't return a favicon iconURL unless we're http or https 258 URL documentURL = m_frame.document()->url(); 259 if (!documentURL.protocolIsInHTTPFamily()) 260 return IconURL(); 261 262 URL url; 263 bool couldSetProtocol = url.setProtocol(documentURL.protocol()); 264 ASSERT_UNUSED(couldSetProtocol, couldSetProtocol); 265 url.setHost(documentURL.host()); 266 if (documentURL.hasPort()) 267 url.setPort(documentURL.port()); 268 269 if (iconType == Favicon) { 270 url.setPath("/favicon.ico"); 271 return IconURL::defaultIconURL(url, Favicon); 272 } 273#if ENABLE(TOUCH_ICON_LOADING) 274 if (iconType == TouchPrecomposedIcon) { 275 url.setPath("/apple-touch-icon-precomposed.png"); 276 return IconURL::defaultIconURL(url, TouchPrecomposedIcon); 277 } 278 if (iconType == TouchIcon) { 279 url.setPath("/apple-touch-icon.png"); 280 return IconURL::defaultIconURL(url, TouchIcon); 281 } 282#endif 283 return IconURL(); 284} 285 286} 287