Home | History | Annotate | Download | only in loader
      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