Changeset 49394 in webkit


Ignore:
Timestamp:
Oct 9, 2009 10:03:48 AM (15 years ago)
Author:
hyatt@apple.com
Message:

Implement beforeload for images. ImageLoadEventSender has been refactored into a more generic
ImageEventSender that can be used by both load and beforeload events. If the document has any
beforeload listeners, then the installation of images onto the renderer becomes asynchronous
and will be held up until the beforeload event can fire at a later date.

Reviewed by Adam Roben.

Both beforeload and load events now fire at the end of the tokenizer write() methods, so that
in the typical parsing case we don't have to put off the beforeload/load events until after
a layout or paint might already have happened. This lets beforeload/load not cause extra
layouts and repaints.

  • dom/ContainerNode.cpp:

(WebCore::ContainerNode::dispatchBeforeLoadEvent):

  • dom/Document.cpp:

(WebCore::Document::implicitClose):
(WebCore::Document::addListenerTypeIfNeeded):

  • dom/Document.h:

(WebCore::Document::):

  • dom/XMLTokenizer.cpp:

(WebCore::XMLTokenizer::write):

  • html/HTMLImageElement.cpp:

(WebCore::HTMLImageElement::attach):

  • html/HTMLInputElement.cpp:

(WebCore::HTMLInputElement::attach):

  • html/HTMLTokenizer.cpp:

(WebCore::HTMLTokenizer::write):

  • loader/ImageLoader.cpp:

(WebCore::ImageBeforeLoadEventSender::ImageBeforeLoadEventSender):
(WebCore::ImageLoadEventSender::ImageLoadEventSender):
(WebCore::beforeLoadEventSender):
(WebCore::ImageLoader::ImageLoader):
(WebCore::ImageLoader::~ImageLoader):
(WebCore::ImageLoader::setImage):
(WebCore::ImageLoader::setLoadingImage):
(WebCore::ImageLoader::updateFromElement):
(WebCore::ImageLoader::notifyFinished):
(WebCore::ImageLoader::dispatchPendingBeforeLoadEvent):
(WebCore::ImageLoader::dispatchPendingEvents):
(WebCore::ImageEventSender::ImageEventSender):
(WebCore::ImageEventSender::dispatchEventSoon):
(WebCore::ImageEventSender::cancelEvent):
(WebCore::ImageEventSender::dispatchPendingEvents):
(WebCore::ImageEventSender::timerFired):

  • loader/ImageLoader.h:

(WebCore::ImageLoader::haveFiredBeforeLoadEvent):

  • wml/WMLImageElement.cpp:

(WebCore::WMLImageElement::attach):

Location:
trunk
Files:
2 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r49393 r49394  
     12009-10-08  Dave Hyatt  <hyatt@apple.com>
     2
     3        Reviewed by Adam Roben.
     4
     5        Implement beforeload for images.  ImageLoadEventSender has been refactored into a more generic
     6        ImageEventSender that can be used by both load and beforeload events.  If the document has any
     7        beforeload listeners, then the installation of images onto the renderer becomes asynchronous
     8        and will be held up until the beforeload event can fire at a later date.
     9
     10        Both beforeload and load events now fire at the end of the tokenizer write() methods, so that
     11        in the typical parsing case we don't have to put off the beforeload/load events until after
     12        a layout or paint might already have happened.  This lets beforeload/load not cause extra
     13        layouts and repaints.
     14
     15        * dom/ContainerNode.cpp:
     16        (WebCore::ContainerNode::dispatchBeforeLoadEvent):
     17        * dom/Document.cpp:
     18        (WebCore::Document::implicitClose):
     19        (WebCore::Document::addListenerTypeIfNeeded):
     20        * dom/Document.h:
     21        (WebCore::Document::):
     22        * dom/XMLTokenizer.cpp:
     23        (WebCore::XMLTokenizer::write):
     24        * html/HTMLImageElement.cpp:
     25        (WebCore::HTMLImageElement::attach):
     26        * html/HTMLInputElement.cpp:
     27        (WebCore::HTMLInputElement::attach):
     28        * html/HTMLTokenizer.cpp:
     29        (WebCore::HTMLTokenizer::write):
     30        * loader/ImageLoader.cpp:
     31        (WebCore::ImageBeforeLoadEventSender::ImageBeforeLoadEventSender):
     32        (WebCore::ImageLoadEventSender::ImageLoadEventSender):
     33        (WebCore::beforeLoadEventSender):
     34        (WebCore::ImageLoader::ImageLoader):
     35        (WebCore::ImageLoader::~ImageLoader):
     36        (WebCore::ImageLoader::setImage):
     37        (WebCore::ImageLoader::setLoadingImage):
     38        (WebCore::ImageLoader::updateFromElement):
     39        (WebCore::ImageLoader::notifyFinished):
     40        (WebCore::ImageLoader::dispatchPendingBeforeLoadEvent):
     41        (WebCore::ImageLoader::dispatchPendingEvents):
     42        (WebCore::ImageEventSender::ImageEventSender):
     43        (WebCore::ImageEventSender::dispatchEventSoon):
     44        (WebCore::ImageEventSender::cancelEvent):
     45        (WebCore::ImageEventSender::dispatchPendingEvents):
     46        (WebCore::ImageEventSender::timerFired):
     47        * loader/ImageLoader.h:
     48        (WebCore::ImageLoader::haveFiredBeforeLoadEvent):
     49        * wml/WMLImageElement.cpp:
     50        (WebCore::WMLImageElement::attach):
     51
    1522009-10-09  Pavel Feldman  <pfeldman@chromium.org>
    253
  • trunk/WebCore/dom/ContainerNode.cpp

    r49313 r49394  
    913913bool ContainerNode::dispatchBeforeLoadEvent(const String& sourceURL)
    914914{
     915    if (!document()->hasListenerType(Document::BEFORELOAD_LISTENER))
     916        return true;
     917
    915918    RefPtr<ContainerNode> protector(this);
    916919    RefPtr<BeforeLoadEvent> beforeLoadEvent = BeforeLoadEvent::create(sourceURL);
  • trunk/WebCore/dom/Document.cpp

    r49211 r49394  
    17121712        f->animation()->resumeAnimations(this);
    17131713
    1714     ImageLoader::dispatchPendingLoadEvents();
     1714    ImageLoader::dispatchPendingEvents();
    17151715    dispatchWindowLoadEvent();
    17161716    dispatchWindowEvent(PageTransitionEvent::create(eventNames().pageshowEvent, false), this);
     
    29612961    else if (eventType == eventNames().webkitTransitionEndEvent)
    29622962        addListenerType(TRANSITIONEND_LISTENER);
     2963    else if (eventType == eventNames().beforeloadEvent)
     2964        addListenerType(BEFORELOAD_LISTENER);
    29632965}
    29642966
  • trunk/WebCore/dom/Document.h

    r48826 r49394  
    616616        ANIMATIONSTART_LISTENER              = 0x200,
    617617        ANIMATIONITERATION_LISTENER          = 0x400,
    618         TRANSITIONEND_LISTENER               = 0x800
     618        TRANSITIONEND_LISTENER               = 0x800,
     619        BEFORELOAD_LISTENER                  = 0x1000
    619620    };
    620621
  • trunk/WebCore/dom/XMLTokenizer.cpp

    r49372 r49394  
    4141#include "HTMLNames.h"
    4242#include "HTMLStyleElement.h"
     43#include "ImageLoader.h"
    4344#include "ProcessingInstruction.h"
    4445#include "ResourceError.h"
     
    106107   
    107108    doWrite(s.toString());
     109   
     110    // After parsing, go ahead and dispatch image beforeload/load events.
     111    ImageLoader::dispatchPendingEvents();
    108112}
    109113
  • trunk/WebCore/html/HTMLImageElement.cpp

    r47367 r49394  
    119119    else if (attrName == onloadAttr)
    120120        setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
     121    else if (attrName == onbeforeloadAttr)
     122        setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
    121123    else if (attrName == compositeAttr) {
    122124        if (!parseCompositeOperator(attr->value(), m_compositeOperator))
     
    168170    HTMLElement::attach();
    169171
    170     if (renderer() && renderer()->isImage()) {
     172    if (renderer() && renderer()->isImage() && m_imageLoader.haveFiredBeforeLoadEvent()) {
    171173        RenderImage* imageObj = toRenderImage(renderer());
    172174        if (imageObj->hasImage())
  • trunk/WebCore/html/HTMLInputElement.cpp

    r49199 r49394  
    863863            m_imageLoader.set(new HTMLImageLoader(this));
    864864        m_imageLoader->updateFromElement();
    865         if (renderer()) {
     865        if (renderer() && m_imageLoader->haveFiredBeforeLoadEvent()) {
    866866            RenderImage* imageObj = toRenderImage(renderer());
    867867            imageObj->setCachedImage(m_imageLoader->image());
  • trunk/WebCore/html/HTMLTokenizer.cpp

    r49372 r49394  
    4444#include "HTMLScriptElement.h"
    4545#include "HTMLViewSourceDocument.h"
     46#include "ImageLoader.h"
    4647#include "InspectorTimelineAgent.h"
    4748#include "MappedAttribute.h"
     
    18031804    if (m_noMoreData && !m_inWrite && !state.loadingExtScript() && !m_executingScript && !m_timer.isActive())
    18041805        end(); // this actually causes us to be deleted
     1806   
     1807    // After parsing, go ahead and dispatch image beforeload/load events.
     1808    ImageLoader::dispatchPendingEvents();
    18051809}
    18061810
  • trunk/WebCore/loader/ImageLoader.cpp

    r42002 r49394  
    3232namespace WebCore {
    3333
    34 class ImageLoadEventSender {
     34class ImageEventSender {
    3535public:
    36     ImageLoadEventSender();
    37 
    38     void dispatchLoadEventSoon(ImageLoader*);
    39     void cancelLoadEvent(ImageLoader*);
    40 
    41     void dispatchPendingLoadEvents();
     36    ImageEventSender(const AtomicString& eventType);
     37
     38    void dispatchEventSoon(ImageLoader*);
     39    void cancelEvent(ImageLoader*);
     40
     41    void dispatchPendingEvents();
    4242
    4343private:
    44     ~ImageLoadEventSender();
    45 
    46     void timerFired(Timer<ImageLoadEventSender>*);
    47 
    48     Timer<ImageLoadEventSender> m_timer;
     44    void timerFired(Timer<ImageEventSender>*);
     45
     46    AtomicString m_eventType;
     47    Timer<ImageEventSender> m_timer;
    4948    Vector<ImageLoader*> m_dispatchSoonList;
    5049    Vector<ImageLoader*> m_dispatchingList;
    5150};
    5251
    53 static ImageLoadEventSender& loadEventSender()
    54 {
    55     DEFINE_STATIC_LOCAL(ImageLoadEventSender, sender, ());
     52static ImageEventSender& beforeLoadEventSender()
     53{
     54    DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent));
     55    return sender;
     56}
     57
     58static ImageEventSender& loadEventSender()
     59{
     60    DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent));
    5661    return sender;
    5762}
     
    6065    : m_element(element)
    6166    , m_image(0)
     67    , m_firedBeforeLoad(true)
    6268    , m_firedLoad(true)
    6369    , m_imageComplete(true)
     
    7076    if (m_image)
    7177        m_image->removeClient(this);
    72     loadEventSender().cancelLoadEvent(this);
     78    if (!m_firedBeforeLoad)
     79        beforeLoadEventSender().cancelEvent(this);
     80    if (!m_firedLoad)
     81        loadEventSender().cancelEvent(this);
    7382}
    7483
     
    7988    if (newImage != oldImage) {
    8089        setLoadingImage(newImage);
     90        m_firedBeforeLoad = true;
    8191        m_firedLoad = true;
    8292        m_imageComplete = true;
     
    90100        if (!renderer->isImage())
    91101            return;
    92 
    93102        toRenderImage(renderer)->resetAnimation();
    94103    }
     
    97106void ImageLoader::setLoadingImage(CachedImage* loadingImage)
    98107{
    99     m_firedLoad = false;
    100     m_imageComplete = false;
    101108    m_image = loadingImage;
     109    m_firedBeforeLoad = !loadingImage;
     110    m_firedLoad = !loadingImage;
     111    m_imageComplete = !loadingImage;
    102112}
    103113
     
    138148    if (newImage != oldImage) {
    139149        setLoadingImage(newImage);
    140         if (newImage)
     150        if (newImage) {
    141151            newImage->addClient(this);
     152            if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER))
     153                dispatchPendingBeforeLoadEvent();
     154            else
     155                beforeLoadEventSender().dispatchEventSoon(this);
     156        }
    142157        if (oldImage)
    143158            oldImage->removeClient(this);
     
    147162        if (!renderer->isImage())
    148163            return;
    149 
    150164        toRenderImage(renderer)->resetAnimation();
    151165    }
     
    162176{
    163177    ASSERT(m_failedLoadURL.isEmpty());
     178
    164179    m_imageComplete = true;
    165 
    166     loadEventSender().dispatchLoadEventSoon(this);
    167 
     180    if (haveFiredBeforeLoadEvent())
     181        updateRenderer();
     182
     183    loadEventSender().dispatchEventSoon(this);
     184}
     185
     186void ImageLoader::updateRenderer()
     187{
    168188    if (RenderObject* renderer = m_element->renderer()) {
    169189        if (!renderer->isImage())
    170190            return;
    171 
    172         toRenderImage(renderer)->setCachedImage(m_image.get());
    173     }
     191        RenderImage* imageRenderer = toRenderImage(renderer);
     192       
     193        // Only update the renderer if it doesn't have an image or if what we have
     194        // is a complete image.  This prevents flickering in the case where a dynamic
     195        // change is happening between two images.
     196        CachedImage* cachedImage = imageRenderer->cachedImage();
     197        if (m_image != cachedImage && (m_imageComplete || !imageRenderer->cachedImage()))
     198            imageRenderer->setCachedImage(m_image.get());
     199    }
     200}
     201
     202void ImageLoader::dispatchPendingBeforeLoadEvent()
     203{
     204    if (m_firedBeforeLoad)
     205        return;
     206    if (!m_image)
     207        return;
     208    if (!m_element->document()->attached())
     209        return;
     210    m_firedBeforeLoad = true;
     211    if (m_element->dispatchBeforeLoadEvent(m_image->url())) {
     212        updateRenderer();
     213        return;
     214    }
     215    if (m_image) {
     216        m_image->removeClient(this);
     217        m_image = 0;
     218    }
     219    loadEventSender().cancelEvent(this);
    174220}
    175221
     
    186232}
    187233
    188 void ImageLoader::dispatchPendingLoadEvents()
    189 {
    190     loadEventSender().dispatchPendingLoadEvents();
    191 }
    192 
    193 ImageLoadEventSender::ImageLoadEventSender()
    194     : m_timer(this, &ImageLoadEventSender::timerFired)
    195 {
    196 }
    197 
    198 void ImageLoadEventSender::dispatchLoadEventSoon(ImageLoader* loader)
     234void ImageLoader::dispatchPendingEvents()
     235{
     236    beforeLoadEventSender().dispatchPendingEvents();
     237    loadEventSender().dispatchPendingEvents();
     238}
     239
     240ImageEventSender::ImageEventSender(const AtomicString& eventType)
     241    : m_eventType(eventType)
     242    , m_timer(this, &ImageEventSender::timerFired)
     243{
     244}
     245
     246void ImageEventSender::dispatchEventSoon(ImageLoader* loader)
    199247{
    200248    m_dispatchSoonList.append(loader);
     
    203251}
    204252
    205 void ImageLoadEventSender::cancelLoadEvent(ImageLoader* loader)
     253void ImageEventSender::cancelEvent(ImageLoader* loader)
    206254{
    207255    // Remove instances of this loader from both lists.
     
    221269}
    222270
    223 void ImageLoadEventSender::dispatchPendingLoadEvents()
     271void ImageEventSender::dispatchPendingEvents()
    224272{
    225273    // Need to avoid re-entering this function; if new dispatches are
     
    234282    size_t size = m_dispatchingList.size();
    235283    for (size_t i = 0; i < size; ++i) {
    236         if (ImageLoader* loader = m_dispatchingList[i])
    237             loader->dispatchPendingLoadEvent();
     284        if (ImageLoader* loader = m_dispatchingList[i]) {
     285            if (m_eventType == eventNames().beforeloadEvent)
     286                loader->dispatchPendingBeforeLoadEvent();
     287            else
     288                loader->dispatchPendingLoadEvent();
     289        }
    238290    }
    239291    m_dispatchingList.clear();
    240292}
    241293
    242 void ImageLoadEventSender::timerFired(Timer<ImageLoadEventSender>*)
    243 {
    244     dispatchPendingLoadEvents();
    245 }
    246 
    247 }
     294void ImageEventSender::timerFired(Timer<ImageEventSender>*)
     295{
     296    dispatchPendingEvents();
     297}
     298
     299}
  • trunk/WebCore/loader/ImageLoader.h

    r41766 r49394  
    5454    void setLoadManually(bool loadManually) { m_loadManually = loadManually; }
    5555
     56    bool haveFiredBeforeLoadEvent() const { return m_firedBeforeLoad; }
    5657    bool haveFiredLoadEvent() const { return m_firedLoad; }
    5758
    58     static void dispatchPendingLoadEvents();
     59    static void dispatchPendingEvents();
    5960
    6061protected:
     
    6566    virtual String sourceURI(const AtomicString&) const = 0;
    6667
    67     friend class ImageLoadEventSender;
     68    friend class ImageEventSender;
     69    void dispatchPendingBeforeLoadEvent();
    6870    void dispatchPendingLoadEvent();
    6971
    7072    void setLoadingImage(CachedImage*);
    7173
     74    void updateRenderer();
     75
    7276    Element* m_element;
    7377    CachedResourceHandle<CachedImage> m_image;
    7478    AtomicString m_failedLoadURL;
     79    bool m_firedBeforeLoad : 1;
    7580    bool m_firedLoad : 1;
    7681    bool m_imageComplete : 1;
  • trunk/WebCore/wml/WMLImageElement.cpp

    r43310 r49394  
    9595    WMLElement::attach();
    9696
    97     if (renderer() && renderer()->isImage()) {
     97    if (renderer() && renderer()->isImage() && m_imageLoader.haveFiredBeforeLoadEvent()) {
    9898        RenderImage* imageObj = toRenderImage(renderer());
    9999        if (imageObj->hasImage())
    100100            return;
    101 
    102101        imageObj->setCachedImage(m_imageLoader.image());
    103 
     102       
    104103        // If we have no image at all because we have no src attribute, set
    105104        // image height and width for the alt text instead.
Note: See TracChangeset for help on using the changeset viewer.