Diff
Modified: trunk/LayoutTests/ChangeLog (251636 => 251637)
--- trunk/LayoutTests/ChangeLog 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/LayoutTests/ChangeLog 2019-10-26 17:17:54 UTC (rev 251637)
@@ -1,3 +1,40 @@
+2019-10-26 Rob Buis <[email protected]>
+
+ Main implementation for lazy image loading
+ https://bugs.webkit.org/show_bug.cgi?id=200764
+
+ Reviewed by Simon Fraser.
+
+ 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:
+
2019-10-26 Yury Semikhatsky <[email protected]>
[GTK] LayoutTests/inspector tests timeout
Modified: trunk/LayoutTests/TestExpectations (251636 => 251637)
--- trunk/LayoutTests/TestExpectations 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/LayoutTests/TestExpectations 2019-10-26 17:17:54 UTC (rev 251637)
@@ -3854,7 +3854,6 @@
fast/text/design-system-ui-15.html [ ImageOnlyFailure ]
fast/text/design-system-ui-16.html [ ImageOnlyFailure ]
-webkit.org/b/196698 imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html
webkit.org/b/196698 imported/w3c/web-platform-tests/loading/lazyload/iframe-loading-lazy.tentative.html
webkit.org/b/202517 imported/w3c/web-platform-tests/2dcontext/text-styles/2d.text.draw.baseline.ideographic.html [ Skip ]
Added: trunk/LayoutTests/http/tests/lazyload/attribute-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/attribute-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/attribute-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,8 @@
+
+
+PASS Test that document load event is fired
+PASS Test that values for loading are case insensitive
+PASS Test that <img> with loading=lazy or auto or no attribute or invalid value are loaded as a placeholder
+PASS Test that <img> with loading=eager is fully loaded, and not a placeholder
+PASS Test that deferred <img> are fully loaded when lazyload is turned off
+
Added: trunk/LayoutTests/http/tests/lazyload/attribute.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/attribute.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/attribute.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,94 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <div style="height:10000px;"></div>
+ <img id="no_attribute_img" src=''>
+ <img id="auto_attribute_img" src='' loading="auto">
+ <img id="invalid_attribute_img" src='' loading="invalid-value-default">
+ <img id="lazy_attribute_img" src='' loading="lazy">
+ <img id="eager_attribute_img" src='' loading="eager">
+ <img id="auto_attribute_ci_img" src='' loading="Auto">
+ <img id="lazy_attribute_ci_img" src='' loading="Lazy">
+ <img id="eager_attribute_ci_img" src='' loading="Eager">
+</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 lazy_attribute_img = document.getElementById("lazy_attribute_img");
+ var eager_attribute_img = document.getElementById("eager_attribute_img");
+ var auto_attribute_ci_img = document.getElementById("auto_attribute_ci_img");
+ var lazy_attribute_ci_img = document.getElementById("lazy_attribute_ci_img");
+ var eager_attribute_ci_img = document.getElementById("eager_attribute_ci_img");
+
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done());
+ }, "Test that document load event is fired");
+
+ async_test(function(t) {
+ auto_attribute_ci_img.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(auto_attribute_ci_img));
+ }));
+ window.addEventListener("load", t.step_func_done(function() {
+ assert_false(is_image_fully_loaded(lazy_attribute_ci_img));
+ }));
+ eager_attribute_ci_img.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(eager_attribute_ci_img));
+ }));
+ }, "Test that values for loading are case insensitive");
+
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done(function() {
+ assert_false(is_image_fully_loaded(lazy_attribute_img));
+ assert_true(is_image_fully_loaded(no_attribute_img));
+ }));
+ lazy_attribute_img.addEventListener("load",
+ t.unreached_func("Load event should not be fired for below viewport image with loading=lazy"));
+ auto_attribute_img.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(auto_attribute_img));
+ }));
+ no_attribute_img.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(no_attribute_img));
+ }));
+ invalid_attribute_img.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(invalid_attribute_img));
+ }));
+ }, "Test that <img> with loading=lazy or auto or no attribute or invalid value are loaded as a placeholder");
+
+ async_test(function(t) {
+ eager_attribute_img.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(eager_attribute_img));
+ }));
+ }, "Test that <img> with loading=eager is fully loaded, and not a placeholder");
+
+ async_test(function(t) {
+ var complete = 0;
+ var _onload_callback_ = function() {
+ if (++complete == 2) {
+ // The two images with loading=lazy,Lazy attribute are loaded.
+ assert_true(is_image_fully_loaded(lazy_attribute_img));
+ assert_true(is_image_fully_loaded(lazy_attribute_ci_img));
+ t.done();
+ }
+ assert_equals(this.getAttribute('loading'), "eager");
+ };
+ lazy_attribute_img.addEventListener("load", onload_callback);
+ lazy_attribute_ci_img.addEventListener("load", onload_callback);
+ window.addEventListener("load", t.step_func(function() {
+ assert_equals(lazy_attribute_img.getAttribute('loading'), "lazy");
+ assert_equals(lazy_attribute_ci_img.getAttribute('loading'), "Lazy");
+ lazy_attribute_img.loading = 'eager';
+ lazy_attribute_ci_img.loading = 'eager';
+ }));
+ }, "Test that deferred <img> are fully loaded when lazyload is turned off");
+</script>
Added: trunk/LayoutTests/http/tests/lazyload/invisible-image-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/invisible-image-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/invisible-image-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,4 @@
+
+PASS Test that document load event is fired
+PASS Test that invisible images are loaded
+
Added: trunk/LayoutTests/http/tests/lazyload/invisible-image.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/invisible-image.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/invisible-image.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <div style="height:10000px;"></div>
+ <img id="visibility_hidden" style="visibility:hidden;" src=''>
+ <img id="display_none" style="display:none;" src=''>
+ <img id="attribute_hidden" hidden src=''>
+ <img id="js_hidden" src=''>
+ <script>
+ document.getElementById("js_hidden").style = 'display:none;';
+ </script>
+</body>
+
+<script>
+ var visibility_hidden_element = document.getElementById("visibility_hidden");
+ var display_none_element = document.getElementById("display_none");
+ var attribute_hidden_element = document.getElementById("attribute_hidden");
+ var js_hidden_element = document.getElementById("js_hidden");
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done());
+ }, "Test that document load event is fired");
+ async_test(function(t) {
+ let image_fully_loaded_promise = (element) => {
+ return new Promise(resolve => {
+ element.addEventListener("load",
+ t.step_func(() => {
+ assert_true(is_image_fully_loaded(element));
+ resolve();
+ }));
+ });
+ }
+ Promise.all([image_fully_loaded_promise(visibility_hidden_element),
+ image_fully_loaded_promise(display_none_element),
+ image_fully_loaded_promise(attribute_hidden_element),
+ image_fully_loaded_promise(js_hidden_element)]).then(() => {
+ t.done();
+ });
+ }, "Test that invisible images are loaded");
+</script>
+
Added: trunk/LayoutTests/http/tests/lazyload/js-image-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/js-image-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/js-image-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,7 @@
+
+
+PASS Test that load event is fired for JS Image() fetches
+PASS Test that load event is fired for <img> created via JS and attached below viewport
+PASS Test that load event is fired for adopted <img> that is appended below the viewport
+PASS Test that load event is fired for JS Image() decode even with loading set to lazy
+
Added: trunk/LayoutTests/http/tests/lazyload/js-image.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/js-image.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/js-image.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,55 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <div style="height:10000px;"></div>
+</body>
+
+<script>
+ async_test(function(t) {
+ const img = new Image();
+ img._onload_ = t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(img));
+ });
+ img.src = '';
+ }, "Test that load event is fired for JS Image() fetches");
+
+ async_test(function(t) {
+ const attached_img = document.createElement("IMG");
+ document.body.appendChild(attached_img);
+ attached_img._onload_ = t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(attached_img));
+ });
+ attached_img.src = ""
+ }, "Test that load event is fired for <img> created via JS and attached below viewport");
+
+ async_test(function(t) {
+ var iframe = document.createElement('iframe');
+ iframe.srcdoc = "<img loading='lazy' style='width:100px;height:100px'></img>";
+ iframe._onload_ = function() {
+ var iframe_img = iframe.contentDocument.getElementsByTagName('img')[0];
+ var adopted_img = document.adoptNode(iframe_img);
+ document.body.appendChild(adopted_img);
+ adopted_img._onload_ = t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(adopted_img));
+ });
+ adopted_img.src = ""
+ };
+ document.body.appendChild(iframe);
+ }, "Test that load event is fired for adopted <img> that is appended below the viewport");
+
+ async_test(function(t) {
+ const img = new Image();
+ img.loading = "lazy";
+ img.src = '';
+ img.decode().then(() => {
+ assert_true(is_image_fully_loaded(img));
+ t.done();
+ }).catch((encodingError) => {
+ assert_unreached();
+ })
+ }, "Test that load event is fired for JS Image() decode even with loading set to lazy");
+
+</script>
Added: trunk/LayoutTests/http/tests/lazyload/lazy-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/lazy-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/lazy-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,7 @@
+
+
+
+PASS Test that document load event is fired
+PASS Test that <img> in viewport is loaded, and not a placeholder
+PASS Test that <img> below viewport is a placeholder, with lazyimage enabled
+
Added: trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,7 @@
+CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/resources/lazy-load-in-iframe.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
+CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/resources/lazy-load-in-iframe.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
+CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/resources/lazy-load-in-iframe.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
+CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/resources/lazy-load-in-iframe.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
+
+PASS Verify that iframe's with scripting off disallow lazy image loading.
+
Added: trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,17 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+<script>
+ var t = async_test("Verify that iframe's with scripting off disallow lazy image loading.");
+ function finish() {
+ assert_equals(is_image_fully_loaded(document.getElementById('sandboxedframe').contentWindow.document.getElementById('below_viewport')), true);
+ t.done();
+ }
+</script>
+
+<body>
+ <div style="height:10000px;"></div>
+ <iframe id="sandboxedframe" style="visibility:hidden;display:none" sandbox="allow-same-origin" src="" _onload_="finish()"></iframe>
+ <div id="log"></div>
+</body>
Added: trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,3 @@
+
+PASS Verify that iframe's with sandbox='allow-scripts' allow lazy image loading.
+
Added: trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-enabled.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,17 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+<script>
+ var t = async_test("Verify that iframe's with sandbox='allow-scripts' allow lazy image loading.");
+ function finish() {
+ assert_equals(is_image_fully_loaded(document.getElementById('sandboxedframe').contentWindow.document.getElementById('below_viewport')), false);
+ t.done();
+ }
+</script>
+
+<body>
+ <div style="height:10000px;"></div>
+ <iframe id="sandboxedframe" style="visibility:hidden;display:none" sandbox="allow-same-origin allow-scripts" src="" _onload_="finish()"></iframe>
+ <div id="log"></div>
+</body>
Added: trunk/LayoutTests/http/tests/lazyload/lazy.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/lazy.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/lazy.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,41 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <img id="in_viewport" src=''>
+ <div style="height:10000px;"></div>
+ <img id="below_viewport" src='' loading="lazy">
+</body>
+
+<script>
+ var in_viewport_element = document.getElementById("in_viewport");
+ var below_viewport_element = document.getElementById("below_viewport");
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done());
+ }, "Test that document load event is fired");
+
+ async_test(function(t) {
+ in_viewport_element.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(in_viewport_element));
+ }));
+ }, "Test that <img> in viewport is loaded, and not a placeholder");
+
+ async_test(function(t) {
+ var complete = 0;
+ var _onload_callback_ = function() {
+ if (++complete == 2) {
+ // Document and the above viewport image has loaded.
+ assert_false(is_image_fully_loaded(below_viewport_element));
+ t.done();
+ }
+ };
+ window.addEventListener("load", t.step_func(onload_callback));
+ in_viewport_element.addEventListener("load",
+ t.step_func(onload_callback));
+ below_viewport_element.addEventListener("load",
+ t.unreached_func("Load event should not be fired for below viewport image"));
+ }, "Test that <img> below viewport is a placeholder, with lazyimage enabled");
+</script>
Added: trunk/LayoutTests/http/tests/lazyload/lazy2-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/lazy2-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/lazy2-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,7 @@
+
+
+
+PASS Test that document load event is fired
+PASS Test that <img> in viewport is loaded, and not a placeholder
+PASS Test that <img> below viewport is a placeholder, with lazyimage enabled
+
Added: trunk/LayoutTests/http/tests/lazyload/placeholder.js (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/placeholder.js (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/placeholder.js 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,16 @@
+// Returns if the image is complete and fully loaded as a non-placeholder image.
+function is_image_fully_loaded(image) {
+ if (!image.complete) {
+ return false;
+ }
+
+ let canvas = document.createElement('canvas');
+ canvas.width = canvas.height = 1;
+ let canvasContext = canvas.getContext("2d");
+ canvasContext.drawImage(image, 0, 0);
+ let data = "" 0, canvas.width, canvas.height).data;
+
+ // Fully loaded image should not be a placeholder which is drawn as a
+ // translucent gray rectangle in placeholder_image.cc
+ return data[0] != 0xd9 || data[1] != 0xd9 || data[2] != 0xd9;
+}
Added: trunk/LayoutTests/http/tests/lazyload/resources/lazy-load-in-iframe.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/resources/lazy-load-in-iframe.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/resources/lazy-load-in-iframe.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,32 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <img id="in_viewport" src=''>
+ <div style="height:10000px;"></div>
+ <img id="below_viewport" src='' loading="lazy">
+</body>
+
+<script>
+ var in_viewport_element = document.getElementById("in_viewport");
+ var below_viewport_element = document.getElementById("below_viewport");
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done());
+ }, "Test that document load event is fired");
+
+ async_test(function(t) {
+ in_viewport_element.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(in_viewport_element));
+ }));
+ }, "Test that <img> in viewport is loaded, and not a placeholder");
+
+ async_test(function(t) {
+ below_viewport_element.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(in_viewport_element));
+ }));
+ }, "Test that <img> below viewport is loaded, and not a placeholder");
+</script>
Added: trunk/LayoutTests/http/tests/lazyload/scroll-element-moved-from-document-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/scroll-element-moved-from-document-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/scroll-element-moved-from-document-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,4 @@
+
+
+PASS Test that <img> below viewport is not loaded when moved to another document and then scrolled to
+
Added: trunk/LayoutTests/http/tests/lazyload/scroll-element-moved-from-document.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/scroll-element-moved-from-document.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/scroll-element-moved-from-document.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,27 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <img id="in_viewport" src=''>
+ <div style="height:10000px;"></div>
+ <img id="below_viewport" src='' loading="lazy">
+</body>
+
+<script>
+ var in_viewport_element = document.getElementById("in_viewport");
+ var below_viewport_element = document.getElementById("below_viewport");
+
+ async_test(function(t) {
+ var iframe = document.createElement('iframe');
+ iframe.srcdoc = "<body></body>";
+ iframe._onload_ = function() {
+ var adopted_img = iframe.contentDocument.adoptNode(below_viewport_element);
+ iframe.contentDocument.body.appendChild(adopted_img);
+ window.scrollTo(0, 10000);
+ t.step_timeout(function() { assert_false(is_image_fully_loaded(below_viewport_element)); t.done(); }, 1000);
+ };
+ document.body.insertBefore(iframe, in_viewport_element,);
+ }, "Test that <img> below viewport is not loaded when moved to another document and then scrolled to");
+</script>
Added: trunk/LayoutTests/http/tests/lazyload/scroll-element-removed-from-document-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/scroll-element-removed-from-document-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/scroll-element-removed-from-document-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,4 @@
+
+
+PASS Test that <img> below viewport is not loaded when removed from the document and then scrolled to
+
Added: trunk/LayoutTests/http/tests/lazyload/scroll-element-removed-from-document.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/scroll-element-removed-from-document.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/scroll-element-removed-from-document.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,24 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <img id="in_viewport" src=''>
+ <div style="height:10000px;"></div>
+ <img id="below_viewport" src='' loading="lazy">
+</body>
+
+<script>
+ var in_viewport_element = document.getElementById("in_viewport");
+ var below_viewport_element = document.getElementById("below_viewport");
+
+ async_test(function(t) {
+ below_viewport_element.remove();
+ assert_false(is_image_fully_loaded(below_viewport_element));
+ in_viewport_element.addEventListener("load", t.step_func(function() {
+ window.scrollTo(0, 10000);
+ t.step_timeout(function() { assert_false(is_image_fully_loaded(below_viewport_element)); t.done(); }, 1000);
+ }));
+ }, "Test that <img> below viewport is not loaded when removed from the document and then scrolled to");
+</script>
Added: trunk/LayoutTests/http/tests/lazyload/scroll-expected.txt (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/scroll-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/scroll-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,7 @@
+
+
+
+PASS Test that document load event is fired
+PASS Test that <img> in viewport is loaded, and not a placeholder
+PASS Test that <img> below viewport is loaded when scrolled near, and not a placeholder
+
Added: trunk/LayoutTests/http/tests/lazyload/scroll.html (0 => 251637)
--- trunk/LayoutTests/http/tests/lazyload/scroll.html (rev 0)
+++ trunk/LayoutTests/http/tests/lazyload/scroll.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,35 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
+<script src=""
+<script src=""
+<script src=""
+
+<body>
+ <img id="in_viewport" src=''>
+ <div style="height:10000px;"></div>
+ <img id="below_viewport" src='' loading="lazy">
+</body>
+
+<script>
+ var in_viewport_element = document.getElementById("in_viewport");
+ var below_viewport_element = document.getElementById("below_viewport");
+ async_test(function(t) {
+ window.addEventListener("load", t.step_func_done());
+ }, "Test that document load event is fired");
+
+ async_test(function(t) {
+ in_viewport_element.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(in_viewport_element));
+ }));
+ }, "Test that <img> in viewport is loaded, and not a placeholder");
+
+ async_test(function(t) {
+ in_viewport_element.addEventListener("load", t.step_func(function() {
+ below_viewport_element.scrollIntoView();
+ }));
+ below_viewport_element.addEventListener("load",
+ t.step_func_done(function() {
+ assert_true(is_image_fully_loaded(below_viewport_element));
+ }));
+ }, "Test that <img> below viewport is loaded when scrolled near, and not a placeholder");
+</script>
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (251636 => 251637)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2019-10-26 17:17:54 UTC (rev 251637)
@@ -1,3 +1,15 @@
+2019-10-26 Rob Buis <[email protected]>
+
+ Main implementation for lazy image loading
+ https://bugs.webkit.org/show_bug.cgi?id=200764
+
+ Reviewed by Simon Fraser.
+
+ 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:
+
2019-10-26 Simon Fraser <[email protected]>
Import css/css-syntax WPT tests
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative-expected.txt (0 => 251637)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative-expected.txt (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative-expected.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,5 @@
+
+
+
+PASS Test that images with loading='lazy' load once they enter the viewport.
+
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html (251636 => 251637)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/loading/lazyload/image-loading-lazy.tentative.html 2019-10-26 17:17:54 UTC (rev 251637)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:enableLazyImageLoading=true ] -->
<head>
<title>Images with loading='lazy' load when in the viewport</title>
<link rel="author" title="Scott Little" href=""
Modified: trunk/LayoutTests/platform/mac-wk1/TestExpectations (251636 => 251637)
--- trunk/LayoutTests/platform/mac-wk1/TestExpectations 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/LayoutTests/platform/mac-wk1/TestExpectations 2019-10-26 17:17:54 UTC (rev 251637)
@@ -152,6 +152,9 @@
imported/w3c/web-platform-tests/intersection-observer [ Skip ]
intersection-observer [ Skip ]
+http/tests/lazyload [ Skip ]
+imported/w3c/web-platform-tests/loading/lazyload [ Skip ]
+
# testRunner.queueLoad() does not support loading data URLs in Mac WK1
http/tests/security/contentSecurityPolicy/navigate-self-to-data-url.html [ Skip ]
Modified: trunk/LayoutTests/platform/win/TestExpectations (251636 => 251637)
--- trunk/LayoutTests/platform/win/TestExpectations 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/LayoutTests/platform/win/TestExpectations 2019-10-26 17:17:54 UTC (rev 251637)
@@ -4468,3 +4468,7 @@
webkit.org/b/202952 http/tests/security/navigate-when-restoring-cached-page.html [ Timeout ]
webkit.org/b/202953 http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html [ Timeout ]
+
+# IntersectionObserver is off by default
+http/tests/lazyload [ Skip ]
+imported/w3c/web-platform-tests/loading/lazyload [ Skip ]
Modified: trunk/Source/WebCore/ChangeLog (251636 => 251637)
--- trunk/Source/WebCore/ChangeLog 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/ChangeLog 2019-10-26 17:17:54 UTC (rev 251637)
@@ -1,3 +1,77 @@
+2019-10-26 Rob Buis <[email protected]>
+
+ Main implementation for lazy image loading
+ https://bugs.webkit.org/show_bug.cgi?id=200764
+
+ Reviewed by Simon Fraser.
+
+ 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):
+
2019-10-26 Antti Koivisto <[email protected]>
Move StyleResolver::applyProperty to PropertyCascade
Modified: trunk/Source/WebCore/Sources.txt (251636 => 251637)
--- trunk/Source/WebCore/Sources.txt 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/Sources.txt 2019-10-26 17:17:54 UTC (rev 251637)
@@ -1221,6 +1221,7 @@
html/InputTypeNames.cpp
html/LabelableElement.cpp
html/LabelsNodeList.cpp
+html/LazyLoadImageObserver.cpp
html/LinkIconCollector.cpp
html/LinkRelAttribute.cpp
html/MediaController.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (251636 => 251637)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-10-26 17:17:54 UTC (rev 251637)
@@ -3374,6 +3374,7 @@
AA7FEEAD16A4E74B004C0C33 /* JSSpeechSynthesis.h in Headers */ = {isa = PBXBuildFile; fileRef = AA7FEEAC16A4E74B004C0C33 /* JSSpeechSynthesis.h */; };
AAA728F716D1D8BC00D3BBC6 /* WebAccessibilityObjectWrapperIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = AAA728F116D1D8BC00D3BBC6 /* WebAccessibilityObjectWrapperIOS.h */; };
AAC08CF315F941FD00F1E188 /* AccessibilitySVGRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = AAC08CF115F941FC00F1E188 /* AccessibilitySVGRoot.h */; };
+ AAD9D0B521DFA810001B11C7 /* LazyLoadImageObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = AAD9D0B321DFA80E001B11C7 /* LazyLoadImageObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
AB23A32809BBA7D00067CC53 /* BeforeTextInsertedEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = AB23A32609BBA7D00067CC53 /* BeforeTextInsertedEvent.h */; };
AB247A6D0AFD6383003FA5FD /* RenderSlider.h in Headers */ = {isa = PBXBuildFile; fileRef = AB247A6B0AFD6383003FA5FD /* RenderSlider.h */; };
AB31C91E10AE1B8E000C7B92 /* LineClampValue.h in Headers */ = {isa = PBXBuildFile; fileRef = AB31C91D10AE1B8E000C7B92 /* LineClampValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -9134,6 +9135,8 @@
65FEA86809833ADE00BED4AB /* Page.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = Page.cpp; sourceTree = "<group>"; };
697101071C6BE1550018C7F1 /* AccessibilitySVGElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilitySVGElement.cpp; sourceTree = "<group>"; };
697101081C6BE1550018C7F1 /* AccessibilitySVGElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilitySVGElement.h; sourceTree = "<group>"; };
+ AAD9D0B121DFA80C001B11C7 /* LazyLoadImageObserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LazyLoadImageObserver.cpp; sourceTree = "<group>"; };
+ AAD9D0B321DFA80E001B11C7 /* LazyLoadImageObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LazyLoadImageObserver.h; sourceTree = "<group>"; };
6A22E86F1F10418600F546C3 /* InspectorCanvas.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InspectorCanvas.h; sourceTree = "<group>"; };
6A22E8721F1042C400F546C3 /* InspectorCanvas.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorCanvas.cpp; sourceTree = "<group>"; };
6A7279881F16C29B003F39B8 /* InspectorShaderProgram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorShaderProgram.h; sourceTree = "<group>"; };
@@ -21904,6 +21907,8 @@
450CEBEF15073BBE002BB149 /* LabelableElement.h */,
A456FA2411AD4A830020B420 /* LabelsNodeList.cpp */,
A456FA2511AD4A830020B420 /* LabelsNodeList.h */,
+ AAD9D0B121DFA80C001B11C7 /* LazyLoadImageObserver.cpp */,
+ AAD9D0B321DFA80E001B11C7 /* LazyLoadImageObserver.h */,
1A4DA41F1CDD3A8300F4473C /* LinkIconCollector.cpp */,
1A4DA4201CDD3A8300F4473C /* LinkIconCollector.h */,
1A250E0C1CDD632000D0BE63 /* LinkIconType.h */,
@@ -30998,6 +31003,7 @@
6F7CA3C6208C2957002F29AB /* LayoutState.h in Headers */,
11310CF220BA4A320065A8D0 /* LayoutTreeBuilder.h in Headers */,
141DC0481648348F00371E5A /* LayoutUnit.h in Headers */,
+ AAD9D0B521DFA810001B11C7 /* LazyLoadImageObserver.h in Headers */,
CDE8B5ED1A69777300B4B66A /* LegacyCDMPrivateClearKey.h in Headers */,
CDF4B7121E0087AE00E235A2 /* LegacyCDMSession.h in Headers */,
CDE8B5F11A69778B00B4B66A /* LegacyCDMSessionClearKey.h in Headers */,
Modified: trunk/Source/WebCore/dom/Document.cpp (251636 => 251637)
--- trunk/Source/WebCore/dom/Document.cpp 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/dom/Document.cpp 2019-10-26 17:17:54 UTC (rev 251637)
@@ -119,6 +119,7 @@
#include "KeyboardEvent.h"
#include "KeyframeEffect.h"
#include "LayoutDisallowedScope.h"
+#include "LazyLoadImageObserver.h"
#include "LegacySchemeRegistry.h"
#include "LibWebRTCProvider.h"
#include "LoaderStrategy.h"
@@ -8325,4 +8326,11 @@
return *m_textManipulationController;
}
+LazyLoadImageObserver& Document::lazyLoadImageObserver()
+{
+ if (!m_lazyLoadImageObserver)
+ m_lazyLoadImageObserver = makeUnique<LazyLoadImageObserver>();
+ return *m_lazyLoadImageObserver;
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/dom/Document.h (251636 => 251637)
--- trunk/Source/WebCore/dom/Document.h 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/dom/Document.h 2019-10-26 17:17:54 UTC (rev 251637)
@@ -153,6 +153,7 @@
class JSNode;
class LayoutPoint;
class LayoutRect;
+class LazyLoadImageObserver;
class LiveNodeList;
class Locale;
class Location;
@@ -1556,6 +1557,8 @@
WEBCORE_EXPORT TextManipulationController& textManipulationController();
TextManipulationController* textManipulationControllerIfExists() { return m_textManipulationController.get(); }
+ LazyLoadImageObserver& lazyLoadImageObserver();
+
protected:
enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 };
Document(Frame*, const URL&, unsigned = DefaultDocumentClass, unsigned constructionFlags = 0);
@@ -1741,6 +1744,8 @@
Element* m_cssTarget { nullptr };
+ std::unique_ptr<LazyLoadImageObserver> m_lazyLoadImageObserver;
+
RefPtr<SerializedScriptValue> m_pendingStateObject;
MonotonicTime m_documentCreationTime;
bool m_overMinimumLayoutThreshold { false };
Modified: trunk/Source/WebCore/html/HTMLImageElement.cpp (251636 => 251637)
--- trunk/Source/WebCore/html/HTMLImageElement.cpp 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/html/HTMLImageElement.cpp 2019-10-26 17:17:54 UTC (rev 251637)
@@ -43,6 +43,7 @@
#include "HTMLMapElement.h"
#include "HTMLSourceElement.h"
#include "HTMLSrcsetParser.h"
+#include "LazyLoadImageObserver.h"
#include "Logging.h"
#include "MIMETypeRegistry.h"
#include "MediaList.h"
@@ -53,6 +54,7 @@
#include "RenderImage.h"
#include "RenderView.h"
#include "RuntimeEnabledFeatures.h"
+#include "ScriptController.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "SizesAttributeParser.h"
@@ -244,7 +246,10 @@
#endif
} else if (name == x_apple_editable_imageAttr)
updateEditableImage();
- else {
+ else if (name == loadingAttr) {
+ if (!equalLettersIgnoringASCIICase(value, "lazy"))
+ loadDeferredImage();
+ } else {
if (name == nameAttr) {
bool willHaveName = !value.isNull();
if (m_hadNameBeforeAttributeChanged != willHaveName && isConnected() && !isInShadowTree() && is<HTMLDocument>(document())) {
@@ -263,6 +268,11 @@
}
}
+void HTMLImageElement::loadDeferredImage()
+{
+ m_imageLoader->loadDeferredImage();
+}
+
const AtomString& HTMLImageElement::altText() const
{
// lets figure out the alt text.. magic stuff
@@ -665,6 +675,7 @@
void HTMLImageElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
{
+ m_createdByParser = false;
m_imageLoader->elementDidMoveToNewDocument();
HTMLElement::didMoveToNewDocument(oldDocument, newDocument);
}
@@ -861,4 +872,35 @@
return m_imageLoader->pendingDecodePromisesCountForTesting();
}
+const AtomString& HTMLImageElement::loadingForBindings() const
+{
+ static NeverDestroyed<AtomString> autoValue("auto", AtomString::ConstructFromLiteral);
+ static NeverDestroyed<AtomString> eager("eager", AtomString::ConstructFromLiteral);
+ static NeverDestroyed<AtomString> lazy("lazy", AtomString::ConstructFromLiteral);
+ auto attributeValue = attributeWithoutSynchronization(HTMLNames::loadingAttr);
+ if (equalLettersIgnoringASCIICase(attributeValue, "eager"))
+ return eager;
+ if (equalLettersIgnoringASCIICase(attributeValue, "lazy"))
+ return lazy;
+ return autoValue;
}
+
+void HTMLImageElement::setLoadingForBindings(const AtomString& value)
+{
+ setAttributeWithoutSynchronization(loadingAttr, value);
+}
+
+bool HTMLImageElement::isDeferred() const
+{
+ return m_imageLoader->isDeferred();
+}
+
+bool HTMLImageElement::isLazyLoadable() const
+{
+ if (document().frame() && !document().frame()->script().canExecuteScripts(NotAboutToExecuteScript))
+ return false;
+ // Never do lazy loading for image elements created from _javascript_.
+ return createdByParser() && equalLettersIgnoringASCIICase(attributeWithoutSynchronization(HTMLNames::loadingAttr), "lazy");
+}
+
+}
Modified: trunk/Source/WebCore/html/HTMLImageElement.h (251636 => 251637)
--- trunk/Source/WebCore/html/HTMLImageElement.h 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/html/HTMLImageElement.h 2019-10-26 17:17:54 UTC (rev 251637)
@@ -126,8 +126,17 @@
void defaultEventHandler(Event&) final;
+ void loadDeferredImage();
+
bool createdByParser() const { return m_createdByParser; }
+ const AtomString& loadingForBindings() const;
+ void setLoadingForBindings(const AtomString&);
+
+ bool isLazyLoadable() const;
+
+ bool isDeferred() const;
+
bool isDroppedImagePlaceholder() const { return m_isDroppedImagePlaceholder; }
void setIsDroppedImagePlaceholder() { m_isDroppedImagePlaceholder = true; }
Modified: trunk/Source/WebCore/html/HTMLImageElement.idl (251636 => 251637)
--- trunk/Source/WebCore/html/HTMLImageElement.idl 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/html/HTMLImageElement.idl 2019-10-26 17:17:54 UTC (rev 251637)
@@ -44,7 +44,7 @@
[Conditional=ATTACHMENT_ELEMENT, EnabledAtRuntime=AttachmentElement] readonly attribute DOMString attachmentIdentifier;
- [EnabledAtRuntime=LazyImageLoading, CEReactions, Reflect] attribute DOMString loading;
+ [CEReactions, EnabledAtRuntime=LazyImageLoading, ImplementedAs=loadingForBindings] attribute DOMString loading;
// Extensions
readonly attribute boolean complete;
Added: trunk/Source/WebCore/html/LazyLoadImageObserver.cpp (0 => 251637)
--- trunk/Source/WebCore/html/LazyLoadImageObserver.cpp (rev 0)
+++ trunk/Source/WebCore/html/LazyLoadImageObserver.cpp 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LazyLoadImageObserver.h"
+
+#include "Frame.h"
+#include "HTMLImageElement.h"
+#include "IntersectionObserverCallback.h"
+#include "RenderStyle.h"
+
+#include <limits>
+
+namespace WebCore {
+
+class LazyImageLoadIntersectionObserverCallback final : public IntersectionObserverCallback {
+public:
+ static Ref<LazyImageLoadIntersectionObserverCallback> create(Document& document)
+ {
+ return adoptRef(*new LazyImageLoadIntersectionObserverCallback(document));
+ }
+
+private:
+ CallbackResult<void> handleEvent(const Vector<Ref<IntersectionObserverEntry>>& entries, IntersectionObserver&) final
+ {
+ ASSERT(!entries.isEmpty());
+
+ for (auto& entry : entries) {
+ if (!entry->isIntersecting())
+ continue;
+ Element* element = entry->target();
+ if (is<HTMLImageElement>(element)) {
+ downcast<HTMLImageElement>(*element).loadDeferredImage();
+ element->document().lazyLoadImageObserver().unobserve(*element);
+ }
+ }
+ return { };
+ }
+
+ LazyImageLoadIntersectionObserverCallback(Document& document)
+ : IntersectionObserverCallback(&document)
+ {
+ }
+};
+
+void LazyLoadImageObserver::observe(Element& element)
+{
+ auto& observer = element.document().lazyLoadImageObserver();
+ auto* intersectionObserver = observer.intersectionObserver(element.document());
+ if (!intersectionObserver)
+ return;
+ intersectionObserver->observe(element);
+}
+
+void LazyLoadImageObserver::unobserve(Element& element)
+{
+ auto& observer = element.document().lazyLoadImageObserver();
+ ASSERT(observer.isObserved(element));
+ observer.m_lazyLoadIntersectionObserver->unobserve(element);
+}
+
+IntersectionObserver* LazyLoadImageObserver::intersectionObserver(Document& document)
+{
+ if (!m_lazyLoadIntersectionObserver) {
+ auto callback = LazyImageLoadIntersectionObserverCallback::create(document);
+ auto options = IntersectionObserver::Init { nullptr, emptyString(), { } };
+ auto observer = IntersectionObserver::create(document, WTFMove(callback), WTFMove(options));
+ if (observer.hasException())
+ return nullptr;
+ m_lazyLoadIntersectionObserver = observer.returnValue().ptr();
+ }
+ return m_lazyLoadIntersectionObserver.get();
+}
+
+bool LazyLoadImageObserver::isObserved(Element& element) const
+{
+ return m_lazyLoadIntersectionObserver && m_lazyLoadIntersectionObserver->observationTargets().contains(&element);
+}
+
+}
Added: trunk/Source/WebCore/html/LazyLoadImageObserver.h (0 => 251637)
--- trunk/Source/WebCore/html/LazyLoadImageObserver.h (rev 0)
+++ trunk/Source/WebCore/html/LazyLoadImageObserver.h 2019-10-26 17:17:54 UTC (rev 251637)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IntersectionObserver.h"
+
+namespace WebCore {
+
+class Document;
+class Element;
+
+class LazyLoadImageObserver {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ LazyLoadImageObserver() = default;
+
+ static void observe(Element&);
+ static void unobserve(Element&);
+
+private:
+ IntersectionObserver* intersectionObserver(Document&);
+ bool isObserved(Element&) const;
+
+ // The intersection observer responsible for loading the image once it's near
+ // the viewport.
+ RefPtr<IntersectionObserver> m_lazyLoadIntersectionObserver;
+};
+
+} // namespace
Modified: trunk/Source/WebCore/html/parser/HTMLPreloadScanner.cpp (251636 => 251637)
--- trunk/Source/WebCore/html/parser/HTMLPreloadScanner.cpp 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/html/parser/HTMLPreloadScanner.cpp 2019-10-26 17:17:54 UTC (rev 251637)
@@ -161,6 +161,10 @@
if (!LinkLoader::isSupportedType(type.value(), m_typeAttribute))
return nullptr;
+ // Do not preload if lazyload is possible but metadata fetch is disabled.
+ if (equalLettersIgnoringASCIICase(m_lazyloadAttribute, "lazy"))
+ return nullptr;
+
auto request = makeUnique<PreloadRequest>(initiatorFor(m_tagId), m_urlToLoad, predictedBaseURL, type.value(), m_mediaAttribute, m_moduleScript, m_referrerPolicy);
request->setCrossOriginMode(m_crossOriginMode);
request->setNonce(m_nonceAttribute);
@@ -206,6 +210,12 @@
m_sizesAttribute = attributeValue;
break;
}
+ if (RuntimeEnabledFeatures::sharedFeatures().lazyImageLoadingEnabled()) {
+ if (match(attributeName, loadingAttr) && m_lazyloadAttribute.isNull()) {
+ m_lazyloadAttribute = attributeValue;
+ break;
+ }
+ }
processImageAndScriptAttribute(attributeName, attributeValue);
break;
case TagId::Source:
@@ -370,6 +380,7 @@
String m_metaContent;
String m_asAttribute;
String m_typeAttribute;
+ String m_lazyloadAttribute;
bool m_metaIsViewport;
bool m_metaIsDisabledAdaptations;
bool m_inputIsImage;
Modified: trunk/Source/WebCore/loader/ImageLoader.cpp (251636 => 251637)
--- trunk/Source/WebCore/loader/ImageLoader.cpp 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/loader/ImageLoader.cpp 2019-10-26 17:17:54 UTC (rev 251637)
@@ -39,9 +39,11 @@
#include "HTMLParserIdioms.h"
#include "InspectorInstrumentation.h"
#include "JSDOMPromiseDeferred.h"
+#include "LazyLoadImageObserver.h"
#include "Page.h"
#include "RenderImage.h"
#include "RenderSVGImage.h"
+#include "RuntimeEnabledFeatures.h"
#include <wtf/NeverDestroyed.h>
#if ENABLE(VIDEO)
@@ -197,8 +199,16 @@
newImage->setLoading(true);
document.cachedResourceLoader().m_documentResources.set(newImage->url(), newImage.get());
document.cachedResourceLoader().setAutoLoadImages(autoLoadOtherImages);
- } else
- newImage = document.cachedResourceLoader().requestImage(WTFMove(request)).value_or(nullptr);
+ } else {
+ if (m_lazyImageLoadState == LazyImageLoadState::None) {
+ if (is<HTMLImageElement>(element())) {
+ auto& imageElement = downcast<HTMLImageElement>(element());
+ if (imageElement.isLazyLoadable() && RuntimeEnabledFeatures::sharedFeatures().lazyImageLoadingEnabled())
+ m_lazyImageLoadState = LazyImageLoadState::Deferred;
+ }
+ }
+ newImage = document.cachedResourceLoader().requestImage(WTFMove(request), (m_lazyImageLoadState == LazyImageLoadState::Deferred) ? ImageLoading::DeferredUntilVisible : ImageLoading::Immediate).value_or(nullptr);
+ }
// If we do not have an image here, it means that a cross-site
// violation occurred, or that the image was blocked via Content
@@ -251,6 +261,9 @@
} else
updateRenderer();
+ if (m_lazyImageLoadState == LazyImageLoadState::Deferred)
+ LazyLoadImageObserver::observe(element());
+
// If newImage is cached, addClient() will result in the load event
// being queued to fire. Ensure this happens after beforeload is
// dispatched.
@@ -307,6 +320,11 @@
ASSERT(m_failedLoadURL.isEmpty());
ASSERT_UNUSED(resource, &resource == m_image.get());
+ if (m_lazyImageLoadState == LazyImageLoadState::Deferred) {
+ LazyLoadImageObserver::unobserve(element());
+ m_lazyImageLoadState = LazyImageLoadState::FullImage;
+ }
+
m_imageComplete = true;
if (!hasPendingBeforeLoadEvent())
updateRenderer();
@@ -560,4 +578,12 @@
m_failedLoadURL = nullAtom();
}
+void ImageLoader::loadDeferredImage()
+{
+ if (m_lazyImageLoadState != LazyImageLoadState::Deferred)
+ return;
+ m_lazyImageLoadState = LazyImageLoadState::FullImage;
+ updateFromElement();
}
+
+}
Modified: trunk/Source/WebCore/loader/ImageLoader.h (251636 => 251637)
--- trunk/Source/WebCore/loader/ImageLoader.h 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/loader/ImageLoader.h 2019-10-26 17:17:54 UTC (rev 251637)
@@ -75,11 +75,17 @@
static void dispatchPendingLoadEvents();
static void dispatchPendingErrorEvents();
+ void loadDeferredImage();
+
+ bool isDeferred() const { return m_lazyImageLoadState == LazyImageLoadState::Deferred; }
+
protected:
explicit ImageLoader(Element&);
void notifyFinished(CachedResource&) override;
private:
+ enum class LazyImageLoadState { None, Deferred, FullImage };
+
virtual void dispatchLoadEvent() = 0;
virtual String sourceURI(const AtomString&) const = 0;
@@ -114,6 +120,7 @@
bool m_imageComplete : 1;
bool m_loadManually : 1;
bool m_elementIsProtected : 1;
+ LazyImageLoadState m_lazyImageLoadState { LazyImageLoadState::None };
};
}
Modified: trunk/Source/WebCore/loader/cache/CachedImage.h (251636 => 251637)
--- trunk/Source/WebCore/loader/cache/CachedImage.h 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/loader/cache/CachedImage.h 2019-10-26 17:17:54 UTC (rev 251637)
@@ -91,7 +91,9 @@
void removeAllClientsWaitingForAsyncDecoding();
void setForceUpdateImageDataEnabledForTesting(bool enabled) { m_forceUpdateImageDataEnabledForTesting = enabled; }
-
+
+ bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); }
+
private:
void clear();
@@ -128,8 +130,6 @@
// For compatibility, images keep loading even if there are HTTP errors.
bool shouldIgnoreHTTPStatusCodeErrors() const override { return true; }
- bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); }
-
class CachedImageObserver final : public RefCounted<CachedImageObserver>, public ImageObserver {
public:
static Ref<CachedImageObserver> create(CachedImage& image) { return adoptRef(*new CachedImageObserver(image)); }
Modified: trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp (251636 => 251637)
--- trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp 2019-10-26 17:17:54 UTC (rev 251637)
@@ -207,7 +207,7 @@
return m_documentLoader ? m_documentLoader->frame() : nullptr;
}
-ResourceErrorOr<CachedResourceHandle<CachedImage>> CachedResourceLoader::requestImage(CachedResourceRequest&& request)
+ResourceErrorOr<CachedResourceHandle<CachedImage>> CachedResourceLoader::requestImage(CachedResourceRequest&& request, ImageLoading imageLoading)
{
if (Frame* frame = this->frame()) {
if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None) {
@@ -220,8 +220,9 @@
}
}
- auto defer = clientDefersImage(request.resourceRequest().url()) ? DeferOption::DeferredByClient : DeferOption::NoDefer;
- return castCachedResourceTo<CachedImage>(requestResource(CachedResource::Type::ImageResource, WTFMove(request), ForPreload::No, defer));
+ if (imageLoading == ImageLoading::Immediate)
+ imageLoading = clientDefersImage(request.resourceRequest().url());
+ return castCachedResourceTo<CachedImage>(requestResource(CachedResource::Type::ImageResource, WTFMove(request), ForPreload::No, imageLoading));
}
ResourceErrorOr<CachedResourceHandle<CachedFont>> CachedResourceLoader::requestFont(CachedResourceRequest&& request, bool isSVG)
@@ -824,7 +825,7 @@
}
#endif
-ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer)
+ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, ImageLoading imageLoading)
{
if (!frame() || !frame()->page()) {
RELEASE_LOG_IF_ALLOWED("requestResource: failed because no frame or page");
@@ -948,7 +949,7 @@
auto& cookieJar = page.cookieJar();
- RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, defer);
+ RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, imageLoading);
switch (policy) {
case Reload:
memoryCache.remove(*resource);
@@ -1010,7 +1011,7 @@
incrementRequestCount(*resource);
}
- if ((policy != Use || resource->stillNeedsLoad()) && defer == DeferOption::NoDefer) {
+ if ((policy != Use || resource->stillNeedsLoad()) && imageLoading == ImageLoading::Immediate) {
resource->load(*this);
// We don't support immediate loads, but we do support immediate failure.
@@ -1119,7 +1120,7 @@
}
}
-CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, DeferOption defer) const
+CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, ImageLoading imageLoading) const
{
auto& request = cachedResourceRequest.resourceRequest();
@@ -1175,7 +1176,7 @@
ASSERT(!request.isConditional());
// Do not load from cache if images are not enabled. The load for this image will be blocked in CachedImage::load.
- if (defer == DeferOption::DeferredByClient)
+ if (imageLoading == ImageLoading::DeferredUntilVisible)
return Reload;
// Don't reload resources while pasting or if cache mode allows stale resources.
@@ -1320,9 +1321,9 @@
reloadImagesIfNotDeferred();
}
-bool CachedResourceLoader::clientDefersImage(const URL&) const
+ImageLoading CachedResourceLoader::clientDefersImage(const URL&) const
{
- return !m_imagesEnabled;
+ return m_imagesEnabled ? ImageLoading::Immediate : ImageLoading::DeferredUntilVisible;
}
bool CachedResourceLoader::shouldPerformImageLoad(const URL& url) const
@@ -1332,13 +1333,13 @@
bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const
{
- return clientDefersImage(url) || !shouldPerformImageLoad(url);
+ return clientDefersImage(url) == ImageLoading::DeferredUntilVisible || !shouldPerformImageLoad(url);
}
void CachedResourceLoader::reloadImagesIfNotDeferred()
{
for (auto& resource : m_documentResources.values()) {
- if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
+ if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && clientDefersImage(resource->url()) == ImageLoading::Immediate)
downcast<CachedImage>(*resource).load(*this);
}
}
Modified: trunk/Source/WebCore/loader/cache/CachedResourceLoader.h (251636 => 251637)
--- trunk/Source/WebCore/loader/cache/CachedResourceLoader.h 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/loader/cache/CachedResourceLoader.h 2019-10-26 17:17:54 UTC (rev 251637)
@@ -62,6 +62,8 @@
template <typename T>
using ResourceErrorOr = Expected<T, ResourceError>;
+enum class ImageLoading : uint8_t { Immediate, DeferredUntilVisible };
+
// The CachedResourceLoader provides a per-context interface to the MemoryCache
// and enforces a bunch of security checks and rules for resource revalidation.
// Its lifetime is roughly per-DocumentLoader, in that it is generally created
@@ -79,7 +81,7 @@
static Ref<CachedResourceLoader> create(DocumentLoader* documentLoader) { return adoptRef(*new CachedResourceLoader(documentLoader)); }
~CachedResourceLoader();
- ResourceErrorOr<CachedResourceHandle<CachedImage>> requestImage(CachedResourceRequest&&);
+ ResourceErrorOr<CachedResourceHandle<CachedImage>> requestImage(CachedResourceRequest&&, ImageLoading = ImageLoading::Immediate);
ResourceErrorOr<CachedResourceHandle<CachedCSSStyleSheet>> requestCSSStyleSheet(CachedResourceRequest&&);
CachedResourceHandle<CachedCSSStyleSheet> requestUserCSSStyleSheet(Page&, CachedResourceRequest&&);
ResourceErrorOr<CachedResourceHandle<CachedScript>> requestScript(CachedResourceRequest&&);
@@ -163,9 +165,8 @@
explicit CachedResourceLoader(DocumentLoader*);
enum class ForPreload { Yes, No };
- enum class DeferOption { NoDefer, DeferredByClient };
- ResourceErrorOr<CachedResourceHandle<CachedResource>> requestResource(CachedResource::Type, CachedResourceRequest&&, ForPreload = ForPreload::No, DeferOption = DeferOption::NoDefer);
+ ResourceErrorOr<CachedResourceHandle<CachedResource>> requestResource(CachedResource::Type, CachedResourceRequest&&, ForPreload = ForPreload::No, ImageLoading = ImageLoading::Immediate);
CachedResourceHandle<CachedResource> revalidateResource(CachedResourceRequest&&, CachedResource&);
CachedResourceHandle<CachedResource> loadResource(CachedResource::Type, PAL::SessionID, CachedResourceRequest&&, const CookieJar&);
@@ -175,7 +176,7 @@
bool canRequest(CachedResource::Type, const URL&, const CachedResourceRequest&, ForPreload);
enum RevalidationPolicy { Use, Revalidate, Reload, Load };
- RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, CachedResourceRequest&, CachedResource* existingResource, ForPreload, DeferOption) const;
+ RevalidationPolicy determineRevalidationPolicy(CachedResource::Type, CachedResourceRequest&, CachedResource* existingResource, ForPreload, ImageLoading) const;
bool shouldUpdateCachedResourceWithCurrentRequest(const CachedResource&, const CachedResourceRequest&);
CachedResourceHandle<CachedResource> updateCachedResourceWithCurrentRequest(const CachedResource&, CachedResourceRequest&&, const PAL::SessionID&, const CookieJar&);
@@ -185,7 +186,7 @@
void performPostLoadActions();
- bool clientDefersImage(const URL&) const;
+ ImageLoading clientDefersImage(const URL&) const;
void reloadImagesIfNotDeferred();
bool canRequestAfterRedirection(CachedResource::Type, const URL&, const ResourceLoaderOptions&) const;
Modified: trunk/Source/WebCore/rendering/RenderImage.cpp (251636 => 251637)
--- trunk/Source/WebCore/rendering/RenderImage.cpp 2019-10-26 15:15:20 UTC (rev 251636)
+++ trunk/Source/WebCore/rendering/RenderImage.cpp 2019-10-26 17:17:54 UTC (rev 251637)
@@ -423,6 +423,11 @@
context.drawRect(snapRectToDevicePixels(LayoutRect({ paintOffset.x() + leftBorder + leftPadding, paintOffset.y() + topBorder + topPadding }, contentSize), document().deviceScaleFactor()), borderWidth);
}
+static bool isDeferredImage(Element* element)
+{
+ return is<HTMLImageElement>(element) && downcast<HTMLImageElement>(element)->isDeferred();
+}
+
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
GraphicsContext& context = paintInfo.context();
@@ -436,7 +441,7 @@
float deviceScaleFactor = document().deviceScaleFactor();
LayoutUnit missingImageBorderWidth(1 / deviceScaleFactor);
- if (!imageResource().cachedImage() || shouldDisplayBrokenImageIcon()) {
+ if (!imageResource().cachedImage() || isDeferredImage(element()) || shouldDisplayBrokenImageIcon()) {
if (paintInfo.phase == PaintPhase::Selection)
return;