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.
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.
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.
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.
23 #include "ImageLoader.h"
25 #include "CachedImage.h"
26 #include "CachedResourceLoader.h"
27 #include "CrossOriginAccessControl.h"
31 #include "HTMLNames.h"
32 #include "HTMLObjectElement.h"
33 #include "HTMLParserIdioms.h"
34 #include "RenderImage.h"
37 #include "RenderSVGImage.h"
40 #include "RenderVideo.h"
44 // ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail.
47 template<> struct ValueCheck<WebCore::ImageLoader*> {
48 typedef WebCore::ImageLoader* TraitType;
49 static void checkConsistency(const WebCore::ImageLoader* p)
54 ValueCheck<WebCore::Element*>::checkConsistency(p->element());
63 class ImageEventSender {
64 WTF_MAKE_NONCOPYABLE(ImageEventSender); WTF_MAKE_FAST_ALLOCATED;
66 ImageEventSender(const AtomicString& eventType);
68 void dispatchEventSoon(ImageLoader*);
69 void cancelEvent(ImageLoader*);
71 void dispatchPendingEvents();
74 bool hasPendingEvents(ImageLoader* loader) const
76 return m_dispatchSoonList.find(loader) != notFound || m_dispatchingList.find(loader) != notFound;
81 void timerFired(Timer<ImageEventSender>*);
83 AtomicString m_eventType;
84 Timer<ImageEventSender> m_timer;
85 Vector<ImageLoader*> m_dispatchSoonList;
86 Vector<ImageLoader*> m_dispatchingList;
89 static ImageEventSender& beforeLoadEventSender()
91 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent));
95 static ImageEventSender& loadEventSender()
97 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent));
101 ImageLoader::ImageLoader(Element* element)
104 , m_firedBeforeLoad(true)
106 , m_imageComplete(true)
107 , m_loadManually(false)
111 ImageLoader::~ImageLoader()
114 m_image->removeClient(this);
116 ASSERT(!m_firedBeforeLoad || !beforeLoadEventSender().hasPendingEvents(this));
117 if (!m_firedBeforeLoad)
118 beforeLoadEventSender().cancelEvent(this);
120 ASSERT(!m_firedLoad || !loadEventSender().hasPendingEvents(this));
122 loadEventSender().cancelEvent(this);
125 void ImageLoader::setImage(CachedImage* newImage)
127 ASSERT(m_failedLoadURL.isEmpty());
128 CachedImage* oldImage = m_image.get();
129 if (newImage != oldImage) {
131 if (!m_firedBeforeLoad) {
132 beforeLoadEventSender().cancelEvent(this);
133 m_firedBeforeLoad = true;
136 loadEventSender().cancelEvent(this);
139 m_imageComplete = true;
141 newImage->addClient(this);
143 oldImage->removeClient(this);
146 if (RenderImageResource* imageResource = renderImageResource())
147 imageResource->resetAnimation();
150 void ImageLoader::updateFromElement()
152 // If we're not making renderers for the page, then don't load images. We don't want to slow
153 // down the raw HTML parsing case by loading images we don't intend to display.
154 Document* document = m_element->document();
155 if (!document->renderer())
158 AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName());
160 if (attr == m_failedLoadURL)
163 // Do not load any image if the 'src' attribute is missing or if it is
165 CachedImage* newImage = 0;
166 if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
167 ResourceRequest request = ResourceRequest(document->completeURL(sourceURI(attr)));
169 String crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr);
170 if (!crossOriginMode.isNull()) {
171 StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
172 updateRequestForAccessControl(request, document->securityOrigin(), allowCredentials);
175 if (m_loadManually) {
176 bool autoLoadOtherImages = document->cachedResourceLoader()->autoLoadImages();
177 document->cachedResourceLoader()->setAutoLoadImages(false);
178 newImage = new CachedImage(request);
179 newImage->setLoading(true);
180 newImage->setOwningCachedResourceLoader(document->cachedResourceLoader());
181 document->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage);
182 document->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages);
184 newImage = document->cachedResourceLoader()->requestImage(request);
186 // If we do not have an image here, it means that a cross-site
187 // violation occurred.
188 m_failedLoadURL = !newImage ? attr : AtomicString();
189 } else if (!attr.isNull()) // Fire an error event if the url is empty.
190 m_element->dispatchEvent(Event::create(eventNames().errorEvent, false, false));
192 CachedImage* oldImage = m_image.get();
193 if (newImage != oldImage) {
194 if (!m_firedBeforeLoad)
195 beforeLoadEventSender().cancelEvent(this);
197 loadEventSender().cancelEvent(this);
200 m_firedBeforeLoad = !newImage;
201 m_firedLoad = !newImage;
202 m_imageComplete = !newImage;
205 newImage->addClient(this);
206 if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER))
207 dispatchPendingBeforeLoadEvent();
209 beforeLoadEventSender().dispatchEventSoon(this);
212 oldImage->removeClient(this);
215 if (RenderImageResource* imageResource = renderImageResource())
216 imageResource->resetAnimation();
219 void ImageLoader::updateFromElementIgnoringPreviousError()
221 // Clear previous error.
222 m_failedLoadURL = AtomicString();
226 void ImageLoader::notifyFinished(CachedResource* resource)
228 ASSERT(m_failedLoadURL.isEmpty());
229 ASSERT(resource == m_image.get());
231 m_imageComplete = true;
232 if (haveFiredBeforeLoadEvent())
238 if (resource->wasCanceled()) {
243 loadEventSender().dispatchEventSoon(this);
246 RenderImageResource* ImageLoader::renderImageResource()
248 RenderObject* renderer = m_element->renderer();
253 if (renderer->isImage())
254 return toRenderImage(renderer)->imageResource();
257 if (renderer->isSVGImage())
258 return toRenderSVGImage(renderer)->imageResource();
262 if (renderer->isVideo())
263 return toRenderVideo(renderer)->imageResource();
269 void ImageLoader::updateRenderer()
271 RenderImageResource* imageResource = renderImageResource();
276 // Only update the renderer if it doesn't have an image or if what we have
277 // is a complete image. This prevents flickering in the case where a dynamic
278 // change is happening between two images.
279 CachedImage* cachedImage = imageResource->cachedImage();
280 if (m_image != cachedImage && (m_imageComplete || !cachedImage))
281 imageResource->setCachedImage(m_image.get());
284 void ImageLoader::dispatchPendingBeforeLoadEvent()
286 if (m_firedBeforeLoad)
290 if (!m_element->document()->attached())
292 m_firedBeforeLoad = true;
293 if (m_element->dispatchBeforeLoadEvent(m_image->url())) {
298 m_image->removeClient(this);
302 loadEventSender().cancelEvent(this);
305 if (m_element->hasTagName(HTMLNames::objectTag))
306 static_cast<HTMLObjectElement*>(m_element)->renderFallbackContent();
309 void ImageLoader::dispatchPendingLoadEvent()
315 if (!m_element->document()->attached())
321 void ImageLoader::dispatchPendingBeforeLoadEvents()
323 beforeLoadEventSender().dispatchPendingEvents();
326 void ImageLoader::dispatchPendingLoadEvents()
328 loadEventSender().dispatchPendingEvents();
331 void ImageLoader::elementWillMoveToNewOwnerDocument()
336 ImageEventSender::ImageEventSender(const AtomicString& eventType)
337 : m_eventType(eventType)
338 , m_timer(this, &ImageEventSender::timerFired)
342 void ImageEventSender::dispatchEventSoon(ImageLoader* loader)
344 m_dispatchSoonList.append(loader);
345 if (!m_timer.isActive())
346 m_timer.startOneShot(0);
349 void ImageEventSender::cancelEvent(ImageLoader* loader)
351 // Remove instances of this loader from both lists.
352 // Use loops because we allow multiple instances to get into the lists.
353 size_t size = m_dispatchSoonList.size();
354 for (size_t i = 0; i < size; ++i) {
355 if (m_dispatchSoonList[i] == loader)
356 m_dispatchSoonList[i] = 0;
358 size = m_dispatchingList.size();
359 for (size_t i = 0; i < size; ++i) {
360 if (m_dispatchingList[i] == loader)
361 m_dispatchingList[i] = 0;
363 if (m_dispatchSoonList.isEmpty())
367 void ImageEventSender::dispatchPendingEvents()
369 // Need to avoid re-entering this function; if new dispatches are
370 // scheduled before the parent finishes processing the list, they
371 // will set a timer and eventually be processed.
372 if (!m_dispatchingList.isEmpty())
377 m_dispatchSoonList.checkConsistency();
379 m_dispatchingList.swap(m_dispatchSoonList);
380 size_t size = m_dispatchingList.size();
381 for (size_t i = 0; i < size; ++i) {
382 if (ImageLoader* loader = m_dispatchingList[i]) {
383 m_dispatchingList[i] = 0;
384 if (m_eventType == eventNames().beforeloadEvent)
385 loader->dispatchPendingBeforeLoadEvent();
387 loader->dispatchPendingLoadEvent();
390 m_dispatchingList.clear();
393 void ImageEventSender::timerFired(Timer<ImageEventSender>*)
395 dispatchPendingEvents();