Changeset 251637 in webkit


Ignore:
Timestamp:
Oct 26, 2019 10:17:54 AM (4 years ago)
Author:
commit-queue@webkit.org
Message:

Main implementation for lazy image loading
https://bugs.webkit.org/show_bug.cgi?id=200764

Patch by Rob Buis <rbuis@igalia.com> on 2019-10-26
Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

Set correct test option.

  • web-platform-tests/loading/lazyload/image-loading-lazy.tentative-expected.txt: Added.
  • web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html:

Source/WebCore:

Implement lazy image loading as specified here [1]. Lazy image loading
is controlled by the loading attribute on <img>. When the loading attribute is
auto or not specified, the behavior is like before this patch, i.e. loading is
eager.

Not all loading=lazy requests will turn into actual lazy image loads, when
scripting is turned off or images are not http(s), they will not be deferred.

This implementation relies on Intersection Observer and hence works on WK2 only.

Deferred images are painted using a simple outline until fully loaded.

[1] https://github.com/whatwg/html/pull/3752/files

Tests: http/tests/lazyload/attribute.html

http/tests/lazyload/invisible-image.html
http/tests/lazyload/js-image.html
http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled.html
http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled.html
http/tests/lazyload/lazy.html
http/tests/lazyload/scroll-element-moved-from-document.html
http/tests/lazyload/scroll-element-removed-from-document.html
http/tests/lazyload/scroll.html

  • Sources.txt:
  • WebCore.xcodeproj/project.pbxproj:
  • dom/Document.cpp:

(WebCore::Document::lazyLoadImageObserver):

  • dom/Document.h:
  • html/HTMLImageElement.cpp:

(WebCore::HTMLImageElement::parseAttribute):
(WebCore::HTMLImageElement::loadDeferredImage):
(WebCore::HTMLImageElement::didMoveToNewDocument):
(WebCore::HTMLImageElement::loadingForBindings const):
(WebCore::HTMLImageElement::setLoadingForBindings):
(WebCore::HTMLImageElement::isDeferred const):
(WebCore::HTMLImageElement::isLazyLoadable const):

  • html/HTMLImageElement.h:
  • html/HTMLImageElement.idl:
  • html/LazyLoadImageObserver.cpp: Added.

(WebCore::LazyLoadImageObserver::observe):
(WebCore::LazyLoadImageObserver::unobserve):
(WebCore::LazyLoadImageObserver::intersectionObserver):
(WebCore::LazyLoadImageObserver::isObserved const):

  • html/LazyLoadImageObserver.h: Added.
  • html/parser/HTMLPreloadScanner.cpp:

(WebCore::TokenPreloadScanner::StartTagScanner::createPreloadRequest):
(WebCore::TokenPreloadScanner::StartTagScanner::processAttribute):

  • loader/ImageLoader.cpp:

(WebCore::ImageLoader::updateFromElement):
(WebCore::ImageLoader::notifyFinished):
(WebCore::ImageLoader::loadDeferredImage):

  • loader/ImageLoader.h:

(WebCore::ImageLoader::isDeferred const):

  • loader/cache/CachedImage.h:
  • loader/cache/CachedResourceLoader.cpp:

(WebCore::CachedResourceLoader::requestImage):
(WebCore::CachedResourceLoader::requestResource):
(WebCore::CachedResourceLoader::determineRevalidationPolicy const):
(WebCore::CachedResourceLoader::clientDefersImage const):
(WebCore::CachedResourceLoader::shouldDeferImageLoad const):
(WebCore::CachedResourceLoader::reloadImagesIfNotDeferred):

  • loader/cache/CachedResourceLoader.h:
  • rendering/RenderImage.cpp:

(WebCore::isDeferredImage):
(WebCore::RenderImage::paintReplaced):

LayoutTests:

Import relevant tests into http/tests/lazyload.
Skip lazy image load tests for WK1 and windows, which
has IntersectionObserver turned off by default.

  • TestExpectations:
  • http/tests/lazyload/attribute-expected.txt: Added.
  • http/tests/lazyload/attribute.html: Added.
  • http/tests/lazyload/invisible-image-expected.txt: Added.
  • http/tests/lazyload/invisible-image.html: Added.
  • http/tests/lazyload/js-image-expected.txt: Added.
  • http/tests/lazyload/js-image.html: Added.
  • http/tests/lazyload/lazy-expected.txt: Added.
  • http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled-expected.txt: Added.
  • http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled.html: Added.
  • http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled-expected.txt: Added.
  • http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled.html: Added.
  • http/tests/lazyload/lazy.html: Added.
  • http/tests/lazyload/lazy2-expected.txt: Added.
  • http/tests/lazyload/placeholder.js: Added.

(is_image_fully_loaded):

  • http/tests/lazyload/resources/lazy-load-in-iframe.html: Added.
  • http/tests/lazyload/scroll-element-moved-from-document-expected.txt: Added.
  • http/tests/lazyload/scroll-element-moved-from-document.html: Added.
  • http/tests/lazyload/scroll-element-removed-from-document-expected.txt: Added.
  • http/tests/lazyload/scroll-element-removed-from-document.html: Added.
  • http/tests/lazyload/scroll-expected.txt: Added.
  • http/tests/lazyload/scroll.html: Added.
  • platform/mac-wk1/TestExpectations:
  • platform/win/TestExpectations:
Location:
trunk
Files:
26 added
21 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r251631 r251637  
     12019-10-26  Rob Buis  <rbuis@igalia.com>
     2
     3        Main implementation for lazy image loading
     4        https://bugs.webkit.org/show_bug.cgi?id=200764
     5
     6        Reviewed by Simon Fraser.
     7
     8        Import relevant tests into http/tests/lazyload.
     9        Skip lazy image load tests for WK1 and windows, which
     10        has IntersectionObserver turned off by default.
     11
     12        * TestExpectations:
     13        * http/tests/lazyload/attribute-expected.txt: Added.
     14        * http/tests/lazyload/attribute.html: Added.
     15        * http/tests/lazyload/invisible-image-expected.txt: Added.
     16        * http/tests/lazyload/invisible-image.html: Added.
     17        * http/tests/lazyload/js-image-expected.txt: Added.
     18        * http/tests/lazyload/js-image.html: Added.
     19        * http/tests/lazyload/lazy-expected.txt: Added.
     20        * http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled-expected.txt: Added.
     21        * http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled.html: Added.
     22        * http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled-expected.txt: Added.
     23        * http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled.html: Added.
     24        * http/tests/lazyload/lazy.html: Added.
     25        * http/tests/lazyload/lazy2-expected.txt: Added.
     26        * http/tests/lazyload/placeholder.js: Added.
     27        (is_image_fully_loaded):
     28        * http/tests/lazyload/resources/lazy-load-in-iframe.html: Added.
     29        * http/tests/lazyload/scroll-element-moved-from-document-expected.txt: Added.
     30        * http/tests/lazyload/scroll-element-moved-from-document.html: Added.
     31        * http/tests/lazyload/scroll-element-removed-from-document-expected.txt: Added.
     32        * http/tests/lazyload/scroll-element-removed-from-document.html: Added.
     33        * http/tests/lazyload/scroll-expected.txt: Added.
     34        * http/tests/lazyload/scroll.html: Added.
     35        * platform/mac-wk1/TestExpectations:
     36        * platform/win/TestExpectations:
     37
    1382019-10-26  Yury Semikhatsky  <yurys@chromium.org>
    239
  • trunk/LayoutTests/TestExpectations

    r251630 r251637  
    38553855fast/text/design-system-ui-16.html [ ImageOnlyFailure ]
    38563856
    3857 webkit.org/b/196698 imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html
    38583857webkit.org/b/196698 imported/w3c/web-platform-tests/loading/lazyload/iframe-loading-lazy.tentative.html
    38593858
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r251635 r251637  
     12019-10-26  Rob Buis  <rbuis@igalia.com>
     2
     3        Main implementation for lazy image loading
     4        https://bugs.webkit.org/show_bug.cgi?id=200764
     5
     6        Reviewed by Simon Fraser.
     7
     8        Set correct test option.
     9
     10        * web-platform-tests/loading/lazyload/image-loading-lazy.tentative-expected.txt: Added.
     11        * web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html:
     12
    1132019-10-26  Simon Fraser  <simon.fraser@apple.com>
    214
  • trunk/LayoutTests/imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html

    r248404 r251637  
    1 <!DOCTYPE html>
     1<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
    22<head>
    33  <title>Images with loading='lazy' load when in the viewport</title>
  • trunk/LayoutTests/platform/mac-wk1/TestExpectations

    r251597 r251637  
    152152imported/w3c/web-platform-tests/intersection-observer [ Skip ]
    153153intersection-observer [ Skip ]
     154
     155http/tests/lazyload [ Skip ]
     156imported/w3c/web-platform-tests/loading/lazyload [ Skip ]
    154157
    155158# testRunner.queueLoad() does not support loading data URLs in Mac WK1
  • trunk/LayoutTests/platform/win/TestExpectations

    r251628 r251637  
    44694469
    44704470webkit.org/b/202953 http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html [ Timeout ]
     4471
     4472# IntersectionObserver is off by default
     4473http/tests/lazyload [ Skip ]
     4474imported/w3c/web-platform-tests/loading/lazyload [ Skip ]
  • trunk/Source/WebCore/ChangeLog

    r251636 r251637  
     12019-10-26  Rob Buis  <rbuis@igalia.com>
     2
     3        Main implementation for lazy image loading
     4        https://bugs.webkit.org/show_bug.cgi?id=200764
     5
     6        Reviewed by Simon Fraser.
     7
     8        Implement lazy image loading as specified here [1]. Lazy image loading
     9        is controlled by the loading attribute on <img>. When the loading attribute is
     10        auto or not specified, the behavior is like before this patch, i.e. loading is
     11        eager.
     12
     13        Not all loading=lazy requests will turn into actual lazy image loads, when
     14        scripting is turned off or images are not http(s), they will not be deferred.
     15
     16        This implementation relies on Intersection Observer and hence works on WK2 only.
     17
     18        Deferred images are painted using a simple outline until fully loaded.
     19
     20        [1] https://github.com/whatwg/html/pull/3752/files
     21
     22        Tests: http/tests/lazyload/attribute.html
     23               http/tests/lazyload/invisible-image.html
     24               http/tests/lazyload/js-image.html
     25               http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled.html
     26               http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled.html
     27               http/tests/lazyload/lazy.html
     28               http/tests/lazyload/scroll-element-moved-from-document.html
     29               http/tests/lazyload/scroll-element-removed-from-document.html
     30               http/tests/lazyload/scroll.html
     31
     32        * Sources.txt:
     33        * WebCore.xcodeproj/project.pbxproj:
     34        * dom/Document.cpp:
     35        (WebCore::Document::lazyLoadImageObserver):
     36        * dom/Document.h:
     37        * html/HTMLImageElement.cpp:
     38        (WebCore::HTMLImageElement::parseAttribute):
     39        (WebCore::HTMLImageElement::loadDeferredImage):
     40        (WebCore::HTMLImageElement::didMoveToNewDocument):
     41        (WebCore::HTMLImageElement::loadingForBindings const):
     42        (WebCore::HTMLImageElement::setLoadingForBindings):
     43        (WebCore::HTMLImageElement::isDeferred const):
     44        (WebCore::HTMLImageElement::isLazyLoadable const):
     45        * html/HTMLImageElement.h:
     46        * html/HTMLImageElement.idl:
     47        * html/LazyLoadImageObserver.cpp: Added.
     48        (WebCore::LazyLoadImageObserver::observe):
     49        (WebCore::LazyLoadImageObserver::unobserve):
     50        (WebCore::LazyLoadImageObserver::intersectionObserver):
     51        (WebCore::LazyLoadImageObserver::isObserved const):
     52        * html/LazyLoadImageObserver.h: Added.
     53        * html/parser/HTMLPreloadScanner.cpp:
     54        (WebCore::TokenPreloadScanner::StartTagScanner::createPreloadRequest):
     55        (WebCore::TokenPreloadScanner::StartTagScanner::processAttribute):
     56        * loader/ImageLoader.cpp:
     57        (WebCore::ImageLoader::updateFromElement):
     58        (WebCore::ImageLoader::notifyFinished):
     59        (WebCore::ImageLoader::loadDeferredImage):
     60        * loader/ImageLoader.h:
     61        (WebCore::ImageLoader::isDeferred const):
     62        * loader/cache/CachedImage.h:
     63        * loader/cache/CachedResourceLoader.cpp:
     64        (WebCore::CachedResourceLoader::requestImage):
     65        (WebCore::CachedResourceLoader::requestResource):
     66        (WebCore::CachedResourceLoader::determineRevalidationPolicy const):
     67        (WebCore::CachedResourceLoader::clientDefersImage const):
     68        (WebCore::CachedResourceLoader::shouldDeferImageLoad const):
     69        (WebCore::CachedResourceLoader::reloadImagesIfNotDeferred):
     70        * loader/cache/CachedResourceLoader.h:
     71        * rendering/RenderImage.cpp:
     72        (WebCore::isDeferredImage):
     73        (WebCore::RenderImage::paintReplaced):
     74
    1752019-10-26  Antti Koivisto  <antti@apple.com>
    276
  • trunk/Source/WebCore/Sources.txt

    r251623 r251637  
    12221222html/LabelableElement.cpp
    12231223html/LabelsNodeList.cpp
     1224html/LazyLoadImageObserver.cpp
    12241225html/LinkIconCollector.cpp
    12251226html/LinkRelAttribute.cpp
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r251623 r251637  
    33753375                AAA728F716D1D8BC00D3BBC6 /* WebAccessibilityObjectWrapperIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = AAA728F116D1D8BC00D3BBC6 /* WebAccessibilityObjectWrapperIOS.h */; };
    33763376                AAC08CF315F941FD00F1E188 /* AccessibilitySVGRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = AAC08CF115F941FC00F1E188 /* AccessibilitySVGRoot.h */; };
     3377                AAD9D0B521DFA810001B11C7 /* LazyLoadImageObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9D0B321DFA80E001B11C7 /* LazyLoadImageObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
    33773378                AB23A32809BBA7D00067CC53 /* BeforeTextInsertedEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = AB23A32609BBA7D00067CC53 /* BeforeTextInsertedEvent.h */; };
    33783379                AB247A6D0AFD6383003FA5FD /* RenderSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = AB247A6B0AFD6383003FA5FD /* RenderSlider.h */; };
     
    91359136                697101071C6BE1550018C7F1 /* AccessibilitySVGElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilitySVGElement.cpp; sourceTree = "<group>"; };
    91369137                697101081C6BE1550018C7F1 /* AccessibilitySVGElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilitySVGElement.h; sourceTree = "<group>"; };
     9138                AAD9D0B121DFA80C001B11C7 /* LazyLoadImageObserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LazyLoadImageObserver.cpp; sourceTree = "<group>"; };
     9139                AAD9D0B321DFA80E001B11C7 /* LazyLoadImageObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LazyLoadImageObserver.h; sourceTree = "<group>"; };
    91379140                6A22E86F1F10418600F546C3 /* InspectorCanvas.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InspectorCanvas.h; sourceTree = "<group>"; };
    91389141                6A22E8721F1042C400F546C3 /* InspectorCanvas.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorCanvas.cpp; sourceTree = "<group>"; };
     
    2190521908                                A456FA2411AD4A830020B420 /* LabelsNodeList.cpp */,
    2190621909                                A456FA2511AD4A830020B420 /* LabelsNodeList.h */,
     21910                                AAD9D0B121DFA80C001B11C7 /* LazyLoadImageObserver.cpp */,
     21911                                AAD9D0B321DFA80E001B11C7 /* LazyLoadImageObserver.h */,
    2190721912                                1A4DA41F1CDD3A8300F4473C /* LinkIconCollector.cpp */,
    2190821913                                1A4DA4201CDD3A8300F4473C /* LinkIconCollector.h */,
     
    3099931004                                11310CF220BA4A320065A8D0 /* LayoutTreeBuilder.h in Headers */,
    3100031005                                141DC0481648348F00371E5A /* LayoutUnit.h in Headers */,
     31006                                AAD9D0B521DFA810001B11C7 /* LazyLoadImageObserver.h in Headers */,
    3100131007                                CDE8B5ED1A69777300B4B66A /* LegacyCDMPrivateClearKey.h in Headers */,
    3100231008                                CDF4B7121E0087AE00E235A2 /* LegacyCDMSession.h in Headers */,
  • trunk/Source/WebCore/dom/Document.cpp

    r251605 r251637  
    120120#include "KeyframeEffect.h"
    121121#include "LayoutDisallowedScope.h"
     122#include "LazyLoadImageObserver.h"
    122123#include "LegacySchemeRegistry.h"
    123124#include "LibWebRTCProvider.h"
     
    83268327}
    83278328
     8329LazyLoadImageObserver& Document::lazyLoadImageObserver()
     8330{
     8331    if (!m_lazyLoadImageObserver)
     8332        m_lazyLoadImageObserver = makeUnique<LazyLoadImageObserver>();
     8333    return *m_lazyLoadImageObserver;
     8334}
     8335
    83288336} // namespace WebCore
  • trunk/Source/WebCore/dom/Document.h

    r251605 r251637  
    154154class LayoutPoint;
    155155class LayoutRect;
     156class LazyLoadImageObserver;
    156157class LiveNodeList;
    157158class Locale;
     
    15571558    TextManipulationController* textManipulationControllerIfExists() { return m_textManipulationController.get(); }
    15581559
     1560    LazyLoadImageObserver& lazyLoadImageObserver();
     1561
    15591562protected:
    15601563    enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 };
     
    17421745    Element* m_cssTarget { nullptr };
    17431746
     1747    std::unique_ptr<LazyLoadImageObserver> m_lazyLoadImageObserver;
     1748
    17441749    RefPtr<SerializedScriptValue> m_pendingStateObject;
    17451750    MonotonicTime m_documentCreationTime;
  • trunk/Source/WebCore/html/HTMLImageElement.cpp

    r250576 r251637  
    4444#include "HTMLSourceElement.h"
    4545#include "HTMLSrcsetParser.h"
     46#include "LazyLoadImageObserver.h"
    4647#include "Logging.h"
    4748#include "MIMETypeRegistry.h"
     
    5455#include "RenderView.h"
    5556#include "RuntimeEnabledFeatures.h"
     57#include "ScriptController.h"
    5658#include "Settings.h"
    5759#include "ShadowRoot.h"
     
    245247    } else if (name == x_apple_editable_imageAttr)
    246248        updateEditableImage();
    247     else {
     249    else if (name == loadingAttr) {
     250        if (!equalLettersIgnoringASCIICase(value, "lazy"))
     251            loadDeferredImage();
     252    } else {
    248253        if (name == nameAttr) {
    249254            bool willHaveName = !value.isNull();
     
    262267        HTMLElement::parseAttribute(name, value);
    263268    }
     269}
     270
     271void HTMLImageElement::loadDeferredImage()
     272{
     273    m_imageLoader->loadDeferredImage();
    264274}
    265275
     
    666676void HTMLImageElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
    667677{
     678    m_createdByParser = false;
    668679    m_imageLoader->elementDidMoveToNewDocument();
    669680    HTMLElement::didMoveToNewDocument(oldDocument, newDocument);
     
    862873}
    863874
    864 }
     875const AtomString& HTMLImageElement::loadingForBindings() const
     876{
     877    static NeverDestroyed<AtomString> autoValue("auto", AtomString::ConstructFromLiteral);
     878    static NeverDestroyed<AtomString> eager("eager", AtomString::ConstructFromLiteral);
     879    static NeverDestroyed<AtomString> lazy("lazy", AtomString::ConstructFromLiteral);
     880    auto attributeValue = attributeWithoutSynchronization(HTMLNames::loadingAttr);
     881    if (equalLettersIgnoringASCIICase(attributeValue, "eager"))
     882        return eager;
     883    if (equalLettersIgnoringASCIICase(attributeValue, "lazy"))
     884        return lazy;
     885    return autoValue;
     886}
     887
     888void HTMLImageElement::setLoadingForBindings(const AtomString& value)
     889{
     890    setAttributeWithoutSynchronization(loadingAttr, value);
     891}
     892
     893bool HTMLImageElement::isDeferred() const
     894{
     895    return m_imageLoader->isDeferred();
     896}
     897
     898bool HTMLImageElement::isLazyLoadable() const
     899{
     900    if (document().frame() && !document().frame()->script().canExecuteScripts(NotAboutToExecuteScript))
     901        return false;
     902    // Never do lazy loading for image elements created from JavaScript.
     903    return createdByParser() && equalLettersIgnoringASCIICase(attributeWithoutSynchronization(HTMLNames::loadingAttr), "lazy");
     904}
     905
     906}
  • trunk/Source/WebCore/html/HTMLImageElement.h

    r250576 r251637  
    127127    void defaultEventHandler(Event&) final;
    128128
     129    void loadDeferredImage();
     130
    129131    bool createdByParser() const { return m_createdByParser; }
     132
     133    const AtomString& loadingForBindings() const;
     134    void setLoadingForBindings(const AtomString&);
     135
     136    bool isLazyLoadable() const;
     137
     138    bool isDeferred() const;
    130139
    131140    bool isDroppedImagePlaceholder() const { return m_isDroppedImagePlaceholder; }
  • trunk/Source/WebCore/html/HTMLImageElement.idl

    r248409 r251637  
    4545    [Conditional=ATTACHMENT_ELEMENT, EnabledAtRuntime=AttachmentElement] readonly attribute DOMString attachmentIdentifier;
    4646
    47     [EnabledAtRuntime=LazyImageLoading, CEReactions, Reflect] attribute DOMString loading;
     47    [CEReactions, EnabledAtRuntime=LazyImageLoading, ImplementedAs=loadingForBindings] attribute DOMString loading;
    4848
    4949    // Extensions
  • trunk/Source/WebCore/html/parser/HTMLPreloadScanner.cpp

    r248846 r251637  
    162162            return nullptr;
    163163
     164        // Do not preload if lazyload is possible but metadata fetch is disabled.
     165        if (equalLettersIgnoringASCIICase(m_lazyloadAttribute, "lazy"))
     166            return nullptr;
     167
    164168        auto request = makeUnique<PreloadRequest>(initiatorFor(m_tagId), m_urlToLoad, predictedBaseURL, type.value(), m_mediaAttribute, m_moduleScript, m_referrerPolicy);
    165169        request->setCrossOriginMode(m_crossOriginMode);
     
    206210                m_sizesAttribute = attributeValue;
    207211                break;
     212            }
     213            if (RuntimeEnabledFeatures::sharedFeatures().lazyImageLoadingEnabled()) {
     214                if (match(attributeName, loadingAttr) && m_lazyloadAttribute.isNull()) {
     215                    m_lazyloadAttribute = attributeValue;
     216                    break;
     217                }
    208218            }
    209219            processImageAndScriptAttribute(attributeName, attributeValue);
     
    371381    String m_asAttribute;
    372382    String m_typeAttribute;
     383    String m_lazyloadAttribute;
    373384    bool m_metaIsViewport;
    374385    bool m_metaIsDisabledAdaptations;
  • trunk/Source/WebCore/loader/ImageLoader.cpp

    r250735 r251637  
    4040#include "InspectorInstrumentation.h"
    4141#include "JSDOMPromiseDeferred.h"
     42#include "LazyLoadImageObserver.h"
    4243#include "Page.h"
    4344#include "RenderImage.h"
    4445#include "RenderSVGImage.h"
     46#include "RuntimeEnabledFeatures.h"
    4547#include <wtf/NeverDestroyed.h>
    4648
     
    198200            document.cachedResourceLoader().m_documentResources.set(newImage->url(), newImage.get());
    199201            document.cachedResourceLoader().setAutoLoadImages(autoLoadOtherImages);
    200         } else
    201             newImage = document.cachedResourceLoader().requestImage(WTFMove(request)).value_or(nullptr);
     202        } else {
     203            if (m_lazyImageLoadState == LazyImageLoadState::None) {
     204                if (is<HTMLImageElement>(element())) {
     205                    auto& imageElement = downcast<HTMLImageElement>(element());
     206                    if (imageElement.isLazyLoadable() && RuntimeEnabledFeatures::sharedFeatures().lazyImageLoadingEnabled())
     207                        m_lazyImageLoadState = LazyImageLoadState::Deferred;
     208                }
     209            }
     210            newImage = document.cachedResourceLoader().requestImage(WTFMove(request), (m_lazyImageLoadState == LazyImageLoadState::Deferred) ? ImageLoading::DeferredUntilVisible : ImageLoading::Immediate).value_or(nullptr);
     211        }
    202212
    203213        // If we do not have an image here, it means that a cross-site
     
    252262                updateRenderer();
    253263
     264            if (m_lazyImageLoadState == LazyImageLoadState::Deferred)
     265                LazyLoadImageObserver::observe(element());
     266
    254267            // If newImage is cached, addClient() will result in the load event
    255268            // being queued to fire. Ensure this happens after beforeload is
     
    307320    ASSERT(m_failedLoadURL.isEmpty());
    308321    ASSERT_UNUSED(resource, &resource == m_image.get());
     322
     323    if (m_lazyImageLoadState == LazyImageLoadState::Deferred) {
     324        LazyLoadImageObserver::unobserve(element());
     325        m_lazyImageLoadState = LazyImageLoadState::FullImage;
     326    }
    309327
    310328    m_imageComplete = true;
     
    561579}
    562580
    563 }
     581void ImageLoader::loadDeferredImage()
     582{
     583    if (m_lazyImageLoadState != LazyImageLoadState::Deferred)
     584        return;
     585    m_lazyImageLoadState = LazyImageLoadState::FullImage;
     586    updateFromElement();
     587}
     588
     589}
  • trunk/Source/WebCore/loader/ImageLoader.h

    r250735 r251637  
    7676    static void dispatchPendingErrorEvents();
    7777
     78    void loadDeferredImage();
     79
     80    bool isDeferred() const { return m_lazyImageLoadState == LazyImageLoadState::Deferred; }
     81
    7882protected:
    7983    explicit ImageLoader(Element&);
     
    8185
    8286private:
     87    enum class LazyImageLoadState { None, Deferred, FullImage };
     88
    8389    virtual void dispatchLoadEvent() = 0;
    8490    virtual String sourceURI(const AtomString&) const = 0;
     
    115121    bool m_loadManually : 1;
    116122    bool m_elementIsProtected : 1;
     123    LazyImageLoadState m_lazyImageLoadState { LazyImageLoadState::None };
    117124};
    118125
  • trunk/Source/WebCore/loader/cache/CachedImage.h

    r248438 r251637  
    9292
    9393    void setForceUpdateImageDataEnabledForTesting(bool enabled) { m_forceUpdateImageDataEnabledForTesting =  enabled; }
    94    
     94
     95    bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); }
     96
    9597private:
    9698    void clear();
     
    128130    // For compatibility, images keep loading even if there are HTTP errors.
    129131    bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; }
    130 
    131     bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); }
    132132
    133133    class CachedImageObserver final : public RefCounted<CachedImageObserver>, public ImageObserver {
  • trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp

    r251594 r251637  
    208208}
    209209
    210 ResourceErrorOr<CachedResourceHandle<CachedImage>> CachedResourceLoader::requestImage(CachedResourceRequest&& request)
     210ResourceErrorOr<CachedResourceHandle<CachedImage>> CachedResourceLoader::requestImage(CachedResourceRequest&& request, ImageLoading imageLoading)
    211211{
    212212    if (Frame* frame = this->frame()) {
     
    221221    }
    222222
    223     auto defer = clientDefersImage(request.resourceRequest().url()) ? DeferOption::DeferredByClient : DeferOption::NoDefer;
    224     return castCachedResourceTo<CachedImage>(requestResource(CachedResource::Type::ImageResource, WTFMove(request), ForPreload::No, defer));
     223    if (imageLoading == ImageLoading::Immediate)
     224        imageLoading = clientDefersImage(request.resourceRequest().url());
     225    return castCachedResourceTo<CachedImage>(requestResource(CachedResource::Type::ImageResource, WTFMove(request), ForPreload::No, imageLoading));
    225226}
    226227
     
    825826#endif
    826827
    827 ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer)
     828ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, ImageLoading imageLoading)
    828829{
    829830    if (!frame() || !frame()->page()) {
     
    949950    auto& cookieJar = page.cookieJar();
    950951
    951     RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, defer);
     952    RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, imageLoading);
    952953    switch (policy) {
    953954    case Reload:
     
    10111012    }
    10121013
    1013     if ((policy != Use || resource->stillNeedsLoad()) && defer == DeferOption::NoDefer) {
     1014    if ((policy != Use || resource->stillNeedsLoad()) && imageLoading == ImageLoading::Immediate) {
    10141015        resource->load(*this);
    10151016
     
    11201121}
    11211122
    1122 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, DeferOption defer) const
     1123CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, ImageLoading imageLoading) const
    11231124{
    11241125    auto& request = cachedResourceRequest.resourceRequest();
     
    11761177
    11771178    // Do not load from cache if images are not enabled. The load for this image will be blocked in CachedImage::load.
    1178     if (defer == DeferOption::DeferredByClient)
     1179    if (imageLoading == ImageLoading::DeferredUntilVisible)
    11791180        return Reload;
    11801181
     
    13211322}
    13221323
    1323 bool CachedResourceLoader::clientDefersImage(const URL&) const
    1324 {
    1325     return !m_imagesEnabled;
     1324ImageLoading CachedResourceLoader::clientDefersImage(const URL&) const
     1325{
     1326    return m_imagesEnabled ? ImageLoading::Immediate : ImageLoading::DeferredUntilVisible;
    13261327}
    13271328
     
    13331334bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const
    13341335{
    1335     return clientDefersImage(url) || !shouldPerformImageLoad(url);
     1336    return clientDefersImage(url) == ImageLoading::DeferredUntilVisible || !shouldPerformImageLoad(url);
    13361337}
    13371338
     
    13391340{
    13401341    for (auto& resource : m_documentResources.values()) {
    1341         if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
     1342        if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && clientDefersImage(resource->url()) == ImageLoading::Immediate)
    13421343            downcast<CachedImage>(*resource).load(*this);
    13431344    }
  • trunk/Source/WebCore/loader/cache/CachedResourceLoader.h

    r250528 r251637  
    6363using ResourceErrorOr = Expected<T, ResourceError>;
    6464
     65enum class ImageLoading : uint8_t { Immediate, DeferredUntilVisible };
     66
    6567// The CachedResourceLoader provides a per-context interface to the MemoryCache
    6668// and enforces a bunch of security checks and rules for resource revalidation.
     
    8082    ~CachedResourceLoader();
    8183
    82     ResourceErrorOr<CachedResourceHandle<CachedImage>> requestImage(CachedResourceRequest&&);
     84    ResourceErrorOr<CachedResourceHandle<CachedImage>> requestImage(CachedResourceRequest&&, ImageLoading = ImageLoading::Immediate);
    8385    ResourceErrorOr<CachedResourceHandle<CachedCSSStyleSheet>> requestCSSStyleSheet(CachedResourceRequest&&);
    8486    CachedResourceHandle<CachedCSSStyleSheet> requestUserCSSStyleSheet(Page&, CachedResourceRequest&&);
     
    164166
    165167    enum class ForPreload { Yes, No };
    166     enum class DeferOption { NoDefer, DeferredByClient };
    167 
    168     ResourceErrorOr<CachedResourceHandle<CachedResource>> requestResource(CachedResource::Type, CachedResourceRequest&&, ForPreload = ForPreload::No, DeferOption = DeferOption::NoDefer);
     168
     169    ResourceErrorOr<CachedResourceHandle<CachedResource>> requestResource(CachedResource::Type, CachedResourceRequest&&, ForPreload = ForPreload::No, ImageLoading = ImageLoading::Immediate);
    169170    CachedResourceHandle<CachedResource> revalidateResource(CachedResourceRequest&&, CachedResource&);
    170171    CachedResourceHandle<CachedResource> loadResource(CachedResource::Type, PAL::SessionID, CachedResourceRequest&&, const CookieJar&);
     
    176177
    177178    enum RevalidationPolicy { Use, Revalidate, Reload, Load };
    178     RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, CachedResourceRequest&, CachedResource* existingResource, ForPreload, DeferOption) const;
     179    RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, CachedResourceRequest&, CachedResource* existingResource, ForPreload, ImageLoading) const;
    179180
    180181    bool shouldUpdateCachedResourceWithCurrentRequest(const CachedResource&, const CachedResourceRequest&);
     
    186187    void performPostLoadActions();
    187188
    188     bool clientDefersImage(const URL&) const;
     189    ImageLoading clientDefersImage(const URL&) const;
    189190    void reloadImagesIfNotDeferred();
    190191
  • trunk/Source/WebCore/rendering/RenderImage.cpp

    r250238 r251637  
    424424}
    425425
     426static bool isDeferredImage(Element* element)
     427{
     428    return is<HTMLImageElement>(element) && downcast<HTMLImageElement>(element)->isDeferred();
     429}
     430
    426431void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    427432{
     
    437442    LayoutUnit missingImageBorderWidth(1 / deviceScaleFactor);
    438443
    439     if (!imageResource().cachedImage() || shouldDisplayBrokenImageIcon()) {
     444    if (!imageResource().cachedImage() || isDeferredImage(element()) || shouldDisplayBrokenImageIcon()) {
    440445        if (paintInfo.phase == PaintPhase::Selection)
    441446            return;
Note: See TracChangeset for help on using the changeset viewer.