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