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 "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. 34 namespace WTF { 35 36 template<> 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 50 namespace WebCore { 51 52 class ImageEventSender : public Noncopyable { 53 public: 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 65 private: 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 74 static ImageEventSender& beforeLoadEventSender() 75 { 76 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent)); 77 return sender; 78 } 79 80 static ImageEventSender& loadEventSender() 81 { 82 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent)); 83 return sender; 84 } 85 86 ImageLoader::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 96 ImageLoader::~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 110 void 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 138 void 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 201 void ImageLoader::updateFromElementIgnoringPreviousError() 202 { 203 // Clear previous error. 204 m_failedLoadURL = AtomicString(); 205 updateFromElement(); 206 } 207 208 void 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 222 void 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 238 void 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 258 void 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 270 void ImageLoader::dispatchPendingBeforeLoadEvents() 271 { 272 beforeLoadEventSender().dispatchPendingEvents(); 273 } 274 275 void ImageLoader::dispatchPendingLoadEvents() 276 { 277 loadEventSender().dispatchPendingEvents(); 278 } 279 280 ImageEventSender::ImageEventSender(const AtomicString& eventType) 281 : m_eventType(eventType) 282 , m_timer(this, &ImageEventSender::timerFired) 283 { 284 } 285 286 void ImageEventSender::dispatchEventSoon(ImageLoader* loader) 287 { 288 m_dispatchSoonList.append(loader); 289 if (!m_timer.isActive()) 290 m_timer.startOneShot(0); 291 } 292 293 void 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 311 void 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 336 void ImageEventSender::timerFired(Timer<ImageEventSender>*) 337 { 338 dispatchPendingEvents(); 339 } 340 341 } 342