1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22#include "config.h" 23#include "ImageLoader.h" 24 25#include "CSSHelper.h" 26#include "CachedImage.h" 27#include "DocLoader.h" 28#include "Document.h" 29#include "Element.h" 30#include "RenderImage.h" 31 32#if !ASSERT_DISABLED 33// ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail. 34namespace WTF { 35 36template<> struct ValueCheck<WebCore::ImageLoader*> { 37 typedef WebCore::ImageLoader* TraitType; 38 static void checkConsistency(const WebCore::ImageLoader* p) 39 { 40 if (!p) 41 return; 42 ASSERT(p->element()); 43 ValueCheck<WebCore::Element*>::checkConsistency(p->element()); 44 } 45}; 46 47} 48#endif 49 50namespace WebCore { 51 52class ImageEventSender : public Noncopyable { 53public: 54 ImageEventSender(const AtomicString& eventType); 55 56 void dispatchEventSoon(ImageLoader*); 57 void cancelEvent(ImageLoader*); 58 59 void dispatchPendingEvents(); 60 61#if !ASSERT_DISABLED 62 bool hasPendingEvents(ImageLoader* loader) { return m_dispatchSoonList.find(loader) != notFound; } 63#endif 64 65private: 66 void timerFired(Timer<ImageEventSender>*); 67 68 AtomicString m_eventType; 69 Timer<ImageEventSender> m_timer; 70 Vector<ImageLoader*> m_dispatchSoonList; 71 Vector<ImageLoader*> m_dispatchingList; 72}; 73 74static ImageEventSender& beforeLoadEventSender() 75{ 76 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent)); 77 return sender; 78} 79 80static ImageEventSender& loadEventSender() 81{ 82 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent)); 83 return sender; 84} 85 86ImageLoader::ImageLoader(Element* element) 87 : m_element(element) 88 , m_image(0) 89 , m_firedBeforeLoad(true) 90 , m_firedLoad(true) 91 , m_imageComplete(true) 92 , m_loadManually(false) 93{ 94} 95 96ImageLoader::~ImageLoader() 97{ 98 if (m_image) 99 m_image->removeClient(this); 100 101 ASSERT(!m_firedBeforeLoad || !beforeLoadEventSender().hasPendingEvents(this)); 102 if (!m_firedBeforeLoad) 103 beforeLoadEventSender().cancelEvent(this); 104 105 ASSERT(!m_firedLoad || !loadEventSender().hasPendingEvents(this)); 106 if (!m_firedLoad) 107 loadEventSender().cancelEvent(this); 108} 109 110void ImageLoader::setImage(CachedImage* newImage) 111{ 112 ASSERT(m_failedLoadURL.isEmpty()); 113 CachedImage* oldImage = m_image.get(); 114 if (newImage != oldImage) { 115 m_image = newImage; 116 if (!m_firedBeforeLoad) { 117 beforeLoadEventSender().cancelEvent(this); 118 m_firedBeforeLoad = true; 119 } 120 if (!m_firedLoad) { 121 loadEventSender().cancelEvent(this); 122 m_firedLoad = true; 123 } 124 m_imageComplete = true; 125 if (newImage) 126 newImage->addClient(this); 127 if (oldImage) 128 oldImage->removeClient(this); 129 } 130 131 if (RenderObject* renderer = m_element->renderer()) { 132 if (!renderer->isImage()) 133 return; 134 toRenderImage(renderer)->resetAnimation(); 135 } 136} 137 138void ImageLoader::updateFromElement() 139{ 140 // If we're not making renderers for the page, then don't load images. We don't want to slow 141 // down the raw HTML parsing case by loading images we don't intend to display. 142 Document* document = m_element->document(); 143 if (!document->renderer()) 144 return; 145 146 AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName()); 147 148 if (attr == m_failedLoadURL) 149 return; 150 151 // Do not load any image if the 'src' attribute is missing or if it is 152 // an empty string referring to a local file. The latter condition is 153 // a quirk that preserves old behavior that Dashboard widgets 154 // need (<rdar://problem/5994621>). 155 CachedImage* newImage = 0; 156 if (!(attr.isNull() || (attr.isEmpty() && document->baseURI().isLocalFile()))) { 157 if (m_loadManually) { 158 document->docLoader()->setAutoLoadImages(false); 159 newImage = new CachedImage(sourceURI(attr)); 160 newImage->setLoading(true); 161 newImage->setDocLoader(document->docLoader()); 162 document->docLoader()->m_documentResources.set(newImage->url(), newImage); 163 } else 164 newImage = document->docLoader()->requestImage(sourceURI(attr)); 165 166 // If we do not have an image here, it means that a cross-site 167 // violation occurred. 168 m_failedLoadURL = !newImage ? attr : AtomicString(); 169 } 170 171 CachedImage* oldImage = m_image.get(); 172 if (newImage != oldImage) { 173 if (!m_firedBeforeLoad) 174 beforeLoadEventSender().cancelEvent(this); 175 if (!m_firedLoad) 176 loadEventSender().cancelEvent(this); 177 178 m_image = newImage; 179 m_firedBeforeLoad = !newImage; 180 m_firedLoad = !newImage; 181 m_imageComplete = !newImage; 182 183 if (newImage) { 184 newImage->addClient(this); 185 if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER)) 186 dispatchPendingBeforeLoadEvent(); 187 else 188 beforeLoadEventSender().dispatchEventSoon(this); 189 } 190 if (oldImage) 191 oldImage->removeClient(this); 192 } 193 194 if (RenderObject* renderer = m_element->renderer()) { 195 if (!renderer->isImage()) 196 return; 197 toRenderImage(renderer)->resetAnimation(); 198 } 199} 200 201void ImageLoader::updateFromElementIgnoringPreviousError() 202{ 203 // Clear previous error. 204 m_failedLoadURL = AtomicString(); 205 updateFromElement(); 206} 207 208void ImageLoader::notifyFinished(CachedResource*) 209{ 210 ASSERT(m_failedLoadURL.isEmpty()); 211 212 m_imageComplete = true; 213 if (haveFiredBeforeLoadEvent()) 214 updateRenderer(); 215 216 if (m_firedLoad) 217 return; 218 219 loadEventSender().dispatchEventSoon(this); 220} 221 222void ImageLoader::updateRenderer() 223{ 224 if (RenderObject* renderer = m_element->renderer()) { 225 if (!renderer->isImage() && !renderer->isVideo()) 226 return; 227 RenderImage* imageRenderer = toRenderImage(renderer); 228 229 // Only update the renderer if it doesn't have an image or if what we have 230 // is a complete image. This prevents flickering in the case where a dynamic 231 // change is happening between two images. 232 CachedImage* cachedImage = imageRenderer->cachedImage(); 233 if (m_image != cachedImage && (m_imageComplete || !imageRenderer->cachedImage())) 234 imageRenderer->setCachedImage(m_image.get()); 235 } 236} 237 238void ImageLoader::dispatchPendingBeforeLoadEvent() 239{ 240 if (m_firedBeforeLoad) 241 return; 242 if (!m_image) 243 return; 244 if (!m_element->document()->attached()) 245 return; 246 m_firedBeforeLoad = true; 247 if (m_element->dispatchBeforeLoadEvent(m_image->url())) { 248 updateRenderer(); 249 return; 250 } 251 if (m_image) { 252 m_image->removeClient(this); 253 m_image = 0; 254 } 255 loadEventSender().cancelEvent(this); 256} 257 258void ImageLoader::dispatchPendingLoadEvent() 259{ 260 if (m_firedLoad) 261 return; 262 if (!m_image) 263 return; 264 if (!m_element->document()->attached()) 265 return; 266 m_firedLoad = true; 267 dispatchLoadEvent(); 268} 269 270void ImageLoader::dispatchPendingBeforeLoadEvents() 271{ 272 beforeLoadEventSender().dispatchPendingEvents(); 273} 274 275void ImageLoader::dispatchPendingLoadEvents() 276{ 277 loadEventSender().dispatchPendingEvents(); 278} 279 280ImageEventSender::ImageEventSender(const AtomicString& eventType) 281 : m_eventType(eventType) 282 , m_timer(this, &ImageEventSender::timerFired) 283{ 284} 285 286void ImageEventSender::dispatchEventSoon(ImageLoader* loader) 287{ 288 m_dispatchSoonList.append(loader); 289 if (!m_timer.isActive()) 290 m_timer.startOneShot(0); 291} 292 293void ImageEventSender::cancelEvent(ImageLoader* loader) 294{ 295 // Remove instances of this loader from both lists. 296 // Use loops because we allow multiple instances to get into the lists. 297 size_t size = m_dispatchSoonList.size(); 298 for (size_t i = 0; i < size; ++i) { 299 if (m_dispatchSoonList[i] == loader) 300 m_dispatchSoonList[i] = 0; 301 } 302 size = m_dispatchingList.size(); 303 for (size_t i = 0; i < size; ++i) { 304 if (m_dispatchingList[i] == loader) 305 m_dispatchingList[i] = 0; 306 } 307 if (m_dispatchSoonList.isEmpty()) 308 m_timer.stop(); 309} 310 311void ImageEventSender::dispatchPendingEvents() 312{ 313 // Need to avoid re-entering this function; if new dispatches are 314 // scheduled before the parent finishes processing the list, they 315 // will set a timer and eventually be processed. 316 if (!m_dispatchingList.isEmpty()) 317 return; 318 319 m_timer.stop(); 320 321 m_dispatchSoonList.checkConsistency(); 322 323 m_dispatchingList.swap(m_dispatchSoonList); 324 size_t size = m_dispatchingList.size(); 325 for (size_t i = 0; i < size; ++i) { 326 if (ImageLoader* loader = m_dispatchingList[i]) { 327 if (m_eventType == eventNames().beforeloadEvent) 328 loader->dispatchPendingBeforeLoadEvent(); 329 else 330 loader->dispatchPendingLoadEvent(); 331 } 332 } 333 m_dispatchingList.clear(); 334} 335 336void ImageEventSender::timerFired(Timer<ImageEventSender>*) 337{ 338 dispatchPendingEvents(); 339} 340 341} 342