1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) 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 "CachedImage.h" 26 #include "CachedResourceLoader.h" 27 #include "Document.h" 28 #include "Element.h" 29 #include "HTMLNames.h" 30 #include "HTMLObjectElement.h" 31 #include "RenderImage.h" 32 33 #if ENABLE(SVG) 34 #include "RenderSVGImage.h" 35 #endif 36 #if ENABLE(VIDEO) 37 #include "RenderVideo.h" 38 #endif 39 40 #if !ASSERT_DISABLED 41 // ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail. 42 namespace WTF { 43 44 template<> struct ValueCheck<WebCore::ImageLoader*> { 45 typedef WebCore::ImageLoader* TraitType; 46 static void checkConsistency(const WebCore::ImageLoader* p) 47 { 48 if (!p) 49 return; 50 ASSERT(p->element()); 51 ValueCheck<WebCore::Element*>::checkConsistency(p->element()); 52 } 53 }; 54 55 } 56 #endif 57 58 namespace WebCore { 59 60 class ImageEventSender { 61 WTF_MAKE_NONCOPYABLE(ImageEventSender); WTF_MAKE_FAST_ALLOCATED; 62 public: 63 ImageEventSender(const AtomicString& eventType); 64 65 void dispatchEventSoon(ImageLoader*); 66 void cancelEvent(ImageLoader*); 67 68 void dispatchPendingEvents(); 69 70 #if !ASSERT_DISABLED 71 bool hasPendingEvents(ImageLoader* loader) { return m_dispatchSoonList.find(loader) != notFound; } 72 #endif 73 74 private: 75 void timerFired(Timer<ImageEventSender>*); 76 77 AtomicString m_eventType; 78 Timer<ImageEventSender> m_timer; 79 Vector<ImageLoader*> m_dispatchSoonList; 80 Vector<ImageLoader*> m_dispatchingList; 81 }; 82 83 static ImageEventSender& beforeLoadEventSender() 84 { 85 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent)); 86 return sender; 87 } 88 89 static ImageEventSender& loadEventSender() 90 { 91 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent)); 92 return sender; 93 } 94 95 ImageLoader::ImageLoader(Element* element) 96 : m_element(element) 97 , m_image(0) 98 , m_firedBeforeLoad(true) 99 , m_firedLoad(true) 100 , m_imageComplete(true) 101 , m_loadManually(false) 102 { 103 } 104 105 ImageLoader::~ImageLoader() 106 { 107 if (m_image) 108 m_image->removeClient(this); 109 110 ASSERT(!m_firedBeforeLoad || !beforeLoadEventSender().hasPendingEvents(this)); 111 if (!m_firedBeforeLoad) 112 beforeLoadEventSender().cancelEvent(this); 113 114 ASSERT(!m_firedLoad || !loadEventSender().hasPendingEvents(this)); 115 if (!m_firedLoad) 116 loadEventSender().cancelEvent(this); 117 } 118 119 void ImageLoader::setImage(CachedImage* newImage) 120 { 121 ASSERT(m_failedLoadURL.isEmpty()); 122 CachedImage* oldImage = m_image.get(); 123 if (newImage != oldImage) { 124 m_image = newImage; 125 if (!m_firedBeforeLoad) { 126 beforeLoadEventSender().cancelEvent(this); 127 m_firedBeforeLoad = true; 128 } 129 if (!m_firedLoad) { 130 loadEventSender().cancelEvent(this); 131 m_firedLoad = true; 132 } 133 m_imageComplete = true; 134 if (newImage) 135 newImage->addClient(this); 136 if (oldImage) 137 oldImage->removeClient(this); 138 } 139 140 if (RenderImageResource* imageResource = renderImageResource()) 141 imageResource->resetAnimation(); 142 } 143 144 void ImageLoader::updateFromElement() 145 { 146 // If we're not making renderers for the page, then don't load images. We don't want to slow 147 // down the raw HTML parsing case by loading images we don't intend to display. 148 Document* document = m_element->document(); 149 if (!document->renderer()) 150 return; 151 152 AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName()); 153 154 if (attr == m_failedLoadURL) 155 return; 156 157 // Do not load any image if the 'src' attribute is missing or if it is 158 // an empty string referring to a local file. The latter condition is 159 // a quirk that preserves old behavior that Dashboard widgets 160 // need (<rdar://problem/5994621>). 161 CachedImage* newImage = 0; 162 if (!(attr.isNull() || (attr.isEmpty() && document->baseURI().isLocalFile()))) { 163 if (m_loadManually) { 164 bool autoLoadOtherImages = document->cachedResourceLoader()->autoLoadImages(); 165 document->cachedResourceLoader()->setAutoLoadImages(false); 166 newImage = new CachedImage(sourceURI(attr)); 167 newImage->setLoading(true); 168 newImage->setOwningCachedResourceLoader(document->cachedResourceLoader()); 169 document->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage); 170 document->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages); 171 } else 172 newImage = document->cachedResourceLoader()->requestImage(sourceURI(attr)); 173 174 // If we do not have an image here, it means that a cross-site 175 // violation occurred. 176 m_failedLoadURL = !newImage ? attr : AtomicString(); 177 } 178 179 CachedImage* oldImage = m_image.get(); 180 if (newImage != oldImage) { 181 if (!m_firedBeforeLoad) 182 beforeLoadEventSender().cancelEvent(this); 183 if (!m_firedLoad) 184 loadEventSender().cancelEvent(this); 185 186 m_image = newImage; 187 m_firedBeforeLoad = !newImage; 188 m_firedLoad = !newImage; 189 m_imageComplete = !newImage; 190 191 if (newImage) { 192 newImage->addClient(this); 193 if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER)) 194 dispatchPendingBeforeLoadEvent(); 195 else 196 beforeLoadEventSender().dispatchEventSoon(this); 197 } 198 if (oldImage) 199 oldImage->removeClient(this); 200 } 201 202 if (RenderImageResource* imageResource = renderImageResource()) 203 imageResource->resetAnimation(); 204 } 205 206 void ImageLoader::updateFromElementIgnoringPreviousError() 207 { 208 // Clear previous error. 209 m_failedLoadURL = AtomicString(); 210 updateFromElement(); 211 } 212 213 void ImageLoader::notifyFinished(CachedResource*) 214 { 215 ASSERT(m_failedLoadURL.isEmpty()); 216 217 m_imageComplete = true; 218 if (haveFiredBeforeLoadEvent()) 219 updateRenderer(); 220 221 if (m_firedLoad) 222 return; 223 224 loadEventSender().dispatchEventSoon(this); 225 } 226 227 RenderImageResource* ImageLoader::renderImageResource() 228 { 229 RenderObject* renderer = m_element->renderer(); 230 231 if (!renderer) 232 return 0; 233 234 if (renderer->isImage()) 235 return toRenderImage(renderer)->imageResource(); 236 237 #if ENABLE(SVG) 238 if (renderer->isSVGImage()) 239 return toRenderSVGImage(renderer)->imageResource(); 240 #endif 241 242 #if ENABLE(VIDEO) 243 if (renderer->isVideo()) 244 return toRenderVideo(renderer)->imageResource(); 245 #endif 246 247 return 0; 248 } 249 250 void ImageLoader::updateRenderer() 251 { 252 RenderImageResource* imageResource = renderImageResource(); 253 254 if (!imageResource) 255 return; 256 257 // Only update the renderer if it doesn't have an image or if what we have 258 // is a complete image. This prevents flickering in the case where a dynamic 259 // change is happening between two images. 260 CachedImage* cachedImage = imageResource->cachedImage(); 261 if (m_image != cachedImage && (m_imageComplete || !cachedImage)) 262 imageResource->setCachedImage(m_image.get()); 263 } 264 265 void ImageLoader::dispatchPendingBeforeLoadEvent() 266 { 267 if (m_firedBeforeLoad) 268 return; 269 if (!m_image) 270 return; 271 if (!m_element->document()->attached()) 272 return; 273 m_firedBeforeLoad = true; 274 if (m_element->dispatchBeforeLoadEvent(m_image->url())) { 275 updateRenderer(); 276 return; 277 } 278 if (m_image) { 279 m_image->removeClient(this); 280 m_image = 0; 281 } 282 loadEventSender().cancelEvent(this); 283 284 if (m_element->hasTagName(HTMLNames::objectTag)) 285 static_cast<HTMLObjectElement*>(m_element)->renderFallbackContent(); 286 } 287 288 void ImageLoader::dispatchPendingLoadEvent() 289 { 290 if (m_firedLoad) 291 return; 292 if (!m_image) 293 return; 294 if (!m_element->document()->attached()) 295 return; 296 m_firedLoad = true; 297 dispatchLoadEvent(); 298 } 299 300 void ImageLoader::dispatchPendingBeforeLoadEvents() 301 { 302 beforeLoadEventSender().dispatchPendingEvents(); 303 } 304 305 void ImageLoader::dispatchPendingLoadEvents() 306 { 307 loadEventSender().dispatchPendingEvents(); 308 } 309 310 void ImageLoader::elementWillMoveToNewOwnerDocument() 311 { 312 setImage(0); 313 } 314 315 ImageEventSender::ImageEventSender(const AtomicString& eventType) 316 : m_eventType(eventType) 317 , m_timer(this, &ImageEventSender::timerFired) 318 { 319 } 320 321 void ImageEventSender::dispatchEventSoon(ImageLoader* loader) 322 { 323 m_dispatchSoonList.append(loader); 324 if (!m_timer.isActive()) 325 m_timer.startOneShot(0); 326 } 327 328 void ImageEventSender::cancelEvent(ImageLoader* loader) 329 { 330 // Remove instances of this loader from both lists. 331 // Use loops because we allow multiple instances to get into the lists. 332 size_t size = m_dispatchSoonList.size(); 333 for (size_t i = 0; i < size; ++i) { 334 if (m_dispatchSoonList[i] == loader) 335 m_dispatchSoonList[i] = 0; 336 } 337 size = m_dispatchingList.size(); 338 for (size_t i = 0; i < size; ++i) { 339 if (m_dispatchingList[i] == loader) 340 m_dispatchingList[i] = 0; 341 } 342 if (m_dispatchSoonList.isEmpty()) 343 m_timer.stop(); 344 } 345 346 void ImageEventSender::dispatchPendingEvents() 347 { 348 // Need to avoid re-entering this function; if new dispatches are 349 // scheduled before the parent finishes processing the list, they 350 // will set a timer and eventually be processed. 351 if (!m_dispatchingList.isEmpty()) 352 return; 353 354 m_timer.stop(); 355 356 m_dispatchSoonList.checkConsistency(); 357 358 m_dispatchingList.swap(m_dispatchSoonList); 359 size_t size = m_dispatchingList.size(); 360 for (size_t i = 0; i < size; ++i) { 361 if (ImageLoader* loader = m_dispatchingList[i]) { 362 if (m_eventType == eventNames().beforeloadEvent) 363 loader->dispatchPendingBeforeLoadEvent(); 364 else 365 loader->dispatchPendingLoadEvent(); 366 } 367 } 368 m_dispatchingList.clear(); 369 } 370 371 void ImageEventSender::timerFired(Timer<ImageEventSender>*) 372 { 373 dispatchPendingEvents(); 374 } 375 376 } 377