Skip to content

Commit

Permalink
LazyLoad: Implement support for "lazyload" attribute on images
Browse files Browse the repository at this point in the history
This CL implements support for the "lazyload" attribute on images,
according to whatwg/html#3752, and as part of
the LazyLoad feature. The accepted values are:

"off", which causes the browser to avoid lazily loading the <img> element

"on" and "auto", activate the default behavior of lazily load the <img> element

When the attribute is changed to "off", the deferred image loads immediately.

Bug: 875080
Change-Id: I839926a9827d019f23aafc40f8315476fe1b3048
Reviewed-on: https://chromium-review.googlesource.com/1197782
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org>
Commit-Queue: rajendrant <rajendrant@chromium.org>
Cr-Commit-Position: refs/heads/master@{#592599}
  • Loading branch information
rajendrant authored and Commit Bot committed Sep 19, 2018
1 parent 2508fd4 commit 713811d
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 11 deletions.
4 changes: 4 additions & 0 deletions third_party/WebKit/LayoutTests/NeverFixTests
Expand Up @@ -1913,3 +1913,7 @@ media/picture-in-picture/picture-in-picture-interstitial-sizing.html [ WontFix ]

# Tests that use too much storage for automation.
http/tests/cachestorage/large-put.html [ WontFix ]

# Tests that work only when LazyImageLoading feature is enabled.
crbug.com/846170 http/tests/lazyload/lazy.html [ WontFix ]
crbug.com/846170 http/tests/lazyload/attribute.html [ WontFix ]
3 changes: 0 additions & 3 deletions third_party/WebKit/LayoutTests/TestExpectations
Expand Up @@ -5054,9 +5054,6 @@ crbug.com/873873 virtual/outofblink-cors/external/wpt/service-workers/service-wo
crbug.com/873873 virtual/outofblink-cors-ns/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Timeout Pass ]
crbug.com/873873 virtual/service-worker-servicification/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Timeout Pass ]

# Tests that work only when LazyImageLoading feature is enabled.
crbug.com/846170 http/tests/lazyload/lazy.html [ Skip ]

# IntersectionObserverV2 tests run in a virtual test suite
crbug.com/827639 intersection-observer/v2 [ Skip ]
crbug.com/827639 http/tests/intersection-observer/v2 [ Skip ]
Expand Down
76 changes: 76 additions & 0 deletions third_party/WebKit/LayoutTests/http/tests/lazyload/attribute.html
@@ -0,0 +1,76 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="placeholder.js"></script>

<body>
<div style="height:10000px;"></div>
<img id="no_attribute_img" src='../loading/resources/base-image1.png'>
<img id="auto_attribute_img" src='../loading/resources/base-image2.png' lazyload="auto">
<img id="invalid_attribute_img" src='../loading/resources/base-image3.png' lazyload="invalid-value-default">
<img id="on_attribute_img" src='../loading/resources/dup-image1.png' lazyload="on">
<img id="off_attribute_img" src='../loading/resources/dup-image2.png' lazyload="off">
</body>

<script>
var no_attribute_img = document.getElementById("no_attribute_img");
var auto_attribute_img = document.getElementById("auto_attribute_img");
var invalid_attribute_img = document.getElementById("invalid_attribute_img");
var on_attribute_img = document.getElementById("on_attribute_img");
var off_attribute_img = document.getElementById("off_attribute_img");

async_test(function(t) {
window.addEventListener("load", t.step_func_done());
}, "Test that document load event is fired");

async_test(function(t) {
window.addEventListener("load", t.step_func_done(function() {
assert_false(is_image_fully_loaded(on_attribute_img));
assert_false(is_image_fully_loaded(no_attribute_img));
}));
on_attribute_img.addEventListener("load",
t.unreached_func("Load event should not be fired for below viewport image with lazyload=on"));
auto_attribute_img.addEventListener("load",
t.unreached_func("Load event should not be fired for below viewport image with lazyload=auto"));
no_attribute_img.addEventListener("load",
t.unreached_func("Load event should not be fired for below viewport image with no lazyload attribute"));
invalid_attribute_img.addEventListener("load",
t.unreached_func("Load event should not be fired for below viewport image with invalid lazyload attribute"));
}, "Test that <img> with lazyload=on or auto or no attribute or invalid value are loaded as a placeholder");

async_test(function(t) {
off_attribute_img.addEventListener("load",
t.step_func_done(function() {
assert_true(is_image_fully_loaded(off_attribute_img));
}));
}, "Test that <img> with lazyload=off is fully loaded, and not a placeholder");

async_test(function(t) {
var complete = 0;
var onload_callback = function() {
if (++complete == 4) {
// The four images with lazyload=on,auto or default or invalid attribute are loaded.
assert_true(is_image_fully_loaded(no_attribute_img));
assert_true(is_image_fully_loaded(on_attribute_img));
assert_true(is_image_fully_loaded(auto_attribute_img));
assert_true(is_image_fully_loaded(invalid_attribute_img));
t.done();
}
assert_equals("off", this.getAttribute('lazyload'));
};
no_attribute_img.addEventListener("load", onload_callback);
on_attribute_img.addEventListener("load", onload_callback);
auto_attribute_img.addEventListener("load", onload_callback);
invalid_attribute_img.addEventListener("load", onload_callback);
window.addEventListener("load", t.step_func(function() {
assert_equals(null, no_attribute_img.getAttribute('lazyload'));
assert_equals("on", on_attribute_img.getAttribute('lazyload'));
assert_equals("auto", auto_attribute_img.getAttribute('lazyload'));
assert_equals("invalid-value-default", invalid_attribute_img.getAttribute('lazyload'));
no_attribute_img.setAttribute('lazyload', 'off');
on_attribute_img.setAttribute('lazyload', 'off');
auto_attribute_img.setAttribute('lazyload', 'off');
invalid_attribute_img.setAttribute('lazyload', 'off');
}));
}, "Test that deferred <img> are fully loaded when lazyload attribute is turned off");
</script>
3 changes: 3 additions & 0 deletions third_party/blink/renderer/core/html/html_image_element.cc
Expand Up @@ -312,6 +312,9 @@ void HTMLImageElement::ParseAttribute(
if (intrinsic_size_changed && GetLayoutObject() &&
GetLayoutObject()->IsLayoutImage())
ToLayoutImage(GetLayoutObject())->IntrinsicSizeChanged();
} else if (name == lazyloadAttr &&
EqualIgnoringASCIICase(params.new_value, "off")) {
GetImageLoader().LoadDeferredImage(referrer_policy_);
} else {
HTMLElement::ParseAttribute(params);
}
Expand Down
Expand Up @@ -143,6 +143,7 @@ class TokenPreloadScanner::StartTagScanner {
referrer_policy_(kReferrerPolicyDefault),
integrity_attr_set_(false),
integrity_features_(features),
lazyload_attr_set_to_off_(false),
scanner_type_(scanner_type) {
if (Match(tag_impl_, imgTag) || Match(tag_impl_, sourceTag)) {
source_size_ = SizesAttributeParser(media_values_, String()).length();
Expand Down Expand Up @@ -272,6 +273,7 @@ class TokenPreloadScanner::StartTagScanner {
request->SetNonce(nonce_);
request->SetCharset(Charset());
request->SetDefer(defer_);
request->SetIsLazyloadAttrOff(lazyload_attr_set_to_off_);

// The only link tags that should keep the integrity metadata are
// stylesheets until crbug.com/677022 is resolved.
Expand Down Expand Up @@ -336,6 +338,11 @@ class TokenPreloadScanner::StartTagScanner {
} else if (!importance_mode_set_ && Match(attribute_name, importanceAttr) &&
RuntimeEnabledFeatures::PriorityHintsEnabled()) {
SetImportance(attribute_value);
} else if (!lazyload_attr_set_to_off_ &&
Match(attribute_name, lazyloadAttr) &&
RuntimeEnabledFeatures::LazyImageLoadingEnabled() &&
EqualIgnoringASCIICase(attribute_value, "off")) {
lazyload_attr_set_to_off_ = true;
}
}

Expand Down Expand Up @@ -629,6 +636,7 @@ class TokenPreloadScanner::StartTagScanner {
bool integrity_attr_set_;
IntegrityMetadataSet integrity_metadata_;
SubresourceIntegrity::IntegrityFeatures integrity_features_;
bool lazyload_attr_set_to_off_;
TokenPreloadScanner::ScannerType scanner_type_;
};

Expand Down
11 changes: 11 additions & 0 deletions third_party/blink/renderer/core/html/parser/preload_request.cc
Expand Up @@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/html/parser/preload_request.h"

#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/script/document_write_intervention.h"
#include "third_party/blink/renderer/core/script/script_loader.h"
Expand Down Expand Up @@ -100,6 +101,16 @@ Resource* PreloadRequest::Start(Document* document) {
// the async request to the blocked script here.
}

if (resource_type_ == ResourceType::kImage) {
if (const auto* frame = document->Loader()->GetFrame()) {
if (frame->IsClientLoFiAllowed(params.GetResourceRequest())) {
params.SetClientLoFiPlaceholder();
} else if (!is_lazyload_attr_off_ && frame->IsLazyLoadingImageAllowed()) {
params.SetAllowImagePlaceholder();
}
}
}

return document->Loader()->StartPreload(resource_type_, params);
}

Expand Down
Expand Up @@ -121,6 +121,10 @@ class CORE_EXPORT PreloadRequest {
return is_image_set_ == ResourceFetcher::kImageIsImageSet;
}

void SetIsLazyloadAttrOff(bool is_lazyload_attr_off) {
is_lazyload_attr_off_ = is_lazyload_attr_off;
}

private:
PreloadRequest(const String& initiator_name,
const TextPosition& initiator_position,
Expand Down Expand Up @@ -148,7 +152,8 @@ class CORE_EXPORT PreloadRequest {
referrer_policy_(referrer_policy),
referrer_source_(referrer_source),
from_insertion_scanner_(false),
is_image_set_(is_image_set) {}
is_image_set_(is_image_set),
is_lazyload_attr_off_(false) {}

KURL CompleteURL(Document*);

Expand All @@ -171,6 +176,7 @@ class CORE_EXPORT PreloadRequest {
IntegrityMetadataSet integrity_metadata_;
bool from_insertion_scanner_;
ResourceFetcher::IsImageSet is_image_set_;
bool is_lazyload_attr_off_;
};

typedef Vector<std::unique_ptr<PreloadRequest>> PreloadRequestStream;
Expand Down
7 changes: 0 additions & 7 deletions third_party/blink/renderer/core/loader/document_loader.cc
Expand Up @@ -218,13 +218,6 @@ Resource* DocumentLoader::StartPreload(ResourceType type,
Resource* resource = nullptr;
switch (type) {
case ResourceType::kImage:
if (frame_) {
if (frame_->IsClientLoFiAllowed(params.GetResourceRequest())) {
params.SetClientLoFiPlaceholder();
} else if (frame_->IsLazyLoadingImageAllowed()) {
params.SetAllowImagePlaceholder();
}
}
resource = ImageResource::Fetch(params, Fetcher());
break;
case ResourceType::kScript:
Expand Down
3 changes: 3 additions & 0 deletions third_party/blink/renderer/core/loader/image_loader.cc
Expand Up @@ -41,6 +41,7 @@
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html/lazy_load_image_observer.h"
#include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/layout/layout_image.h"
#include "third_party/blink/renderer/core/layout/layout_video.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
Expand Down Expand Up @@ -451,6 +452,8 @@ void ImageLoader::DoUpdateFromElement(BypassMainWorldBehavior bypass_behavior,
params.SetClientLoFiPlaceholder();
} else if (auto* html_image = ToHTMLImageElementOrNull(GetElement())) {
if (html_image->ElementCreatedByParser() &&
!EqualIgnoringASCIICase(
html_image->FastGetAttribute(HTMLNames::lazyloadAttr), "off") &&
frame->IsLazyLoadingImageAllowed()) {
params.SetAllowImagePlaceholder();
lazy_image_load_state_ = LazyImageLoadState::kDeferred;
Expand Down

0 comments on commit 713811d

Please sign in to comment.