Commit be3069fb authored by Nicolás Peña Moreno's avatar Nicolás Peña Moreno Committed by Commit Bot

[LargestContentfulPaint] Ignore full viewport images

Bug: 1133883
Change-Id: Id4aac54f834ca7bcf017e8afbbaa4a151d8492b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2441732
Commit-Queue: Nicolás Peña Moreno <npm@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarAnnie Sullivan <sullivan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812992}
parent 7c287415
...@@ -137,6 +137,7 @@ ImageRecord* ImagePaintTimingDetector::UpdateCandidate() { ...@@ -137,6 +137,7 @@ ImageRecord* ImagePaintTimingDetector::UpdateCandidate() {
void ImagePaintTimingDetector::OnPaintFinished() { void ImagePaintTimingDetector::OnPaintFinished() {
frame_index_++; frame_index_++;
viewport_size_ = base::nullopt;
if (need_update_timing_at_frame_end_) { if (need_update_timing_at_frame_end_) {
need_update_timing_at_frame_end_ = false; need_update_timing_at_frame_end_ = false;
frame_view_->GetPaintTimingDetector() frame_view_->GetPaintTimingDetector()
...@@ -297,6 +298,18 @@ uint64_t ImagePaintTimingDetector::ComputeImageRectSize( ...@@ -297,6 +298,18 @@ uint64_t ImagePaintTimingDetector::ComputeImageRectSize(
FloatRect float_visual_rect = FloatRect float_visual_rect =
frame_view_->GetPaintTimingDetector().BlinkSpaceToDIPs( frame_view_->GetPaintTimingDetector().BlinkSpaceToDIPs(
FloatRect(image_border)); FloatRect(image_border));
if (!viewport_size_.has_value()) {
FloatRect viewport = frame_view_->GetPaintTimingDetector().BlinkSpaceToDIPs(
FloatRect(frame_view_->GetScrollableArea()->VisibleContentRect()));
viewport_size_ = viewport.Size().Area();
}
// An SVG image size is computed with respect to the virtual viewport of the
// SVG, so |rect_size| can be larger than |*viewport_size| in edge cases. If
// the rect occupies the whole viewport, disregard this candidate by saying
// the size is 0.
if (rect_size >= *viewport_size_)
return 0;
rect_size = DownScaleIfIntrinsicSizeIsSmaller( rect_size = DownScaleIfIntrinsicSizeIsSmaller(
rect_size, intrinsic_size.Area(), rect_size, intrinsic_size.Area(),
float_visual_rect.Width() * float_visual_rect.Height()); float_visual_rect.Width() * float_visual_rect.Height());
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_IMAGE_PAINT_TIMING_DETECTOR_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_IMAGE_PAINT_TIMING_DETECTOR_H_
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "third_party/blink/public/web/web_widget_client.h" #include "third_party/blink/public/web/web_widget_client.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h" #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
...@@ -292,6 +293,10 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -292,6 +293,10 @@ class CORE_EXPORT ImagePaintTimingDetector final
void RegisterNotifySwapTime(); void RegisterNotifySwapTime();
void ReportCandidateToTrace(ImageRecord&); void ReportCandidateToTrace(ImageRecord&);
void ReportNoCandidateToTrace(); void ReportNoCandidateToTrace();
// Computes the size of an image for the purpose of LargestContentfulPaint,
// downsizing the size of images with low intrinsic size. Images that occupy
// the full viewport are special-cased and this method returns 0 for them so
// that they are not considered valid candidates.
uint64_t ComputeImageRectSize(const IntRect&, uint64_t ComputeImageRectSize(const IntRect&,
const IntSize&, const IntSize&,
const PropertyTreeStateOrAlias&, const PropertyTreeStateOrAlias&,
...@@ -317,6 +322,11 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -317,6 +322,11 @@ class CORE_EXPORT ImagePaintTimingDetector final
// |FindLargestPaintCandidate| occur during the paint tree walk. // |FindLargestPaintCandidate| occur during the paint tree walk.
bool need_update_timing_at_frame_end_ = false; bool need_update_timing_at_frame_end_ = false;
// We cache the viewport size computation to avoid performing it on every
// image. This value is reset when paint is finished and is computed if unset
// when needed. 0 means that the size has not been computed.
base::Optional<uint64_t> viewport_size_;
ImageRecordsManager records_manager_; ImageRecordsManager records_manager_;
Member<LocalFrameView> frame_view_; Member<LocalFrameView> frame_view_;
Member<PaintTimingCallbackManager> callback_manager_; Member<PaintTimingCallbackManager> callback_manager_;
......
...@@ -8,9 +8,7 @@ ...@@ -8,9 +8,7 @@
<div id='my_div'></div> <div id='my_div'></div>
<script> <script>
async_test(t => { async_test(t => {
if (!window.PerformanceElementTiming) { assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
assert_unreached("PerformanceElementTiming is not implemented");
}
const remote_img = 'http://{{domains[www]}}:{{ports[http][1]}}/element-timing/resources/TAOImage.py?' const remote_img = 'http://{{domains[www]}}:{{ports[http][1]}}/element-timing/resources/TAOImage.py?'
+ 'origin=' + window.location.origin +'&tao='; + 'origin=' + window.location.origin +'&tao=';
const valid_tao = ['wildcard', 'origin', 'multi', 'multi_wildcard', 'match_origin', 'match_wildcard']; const valid_tao = ['wildcard', 'origin', 'multi', 'multi_wildcard', 'match_origin', 'match_wildcard'];
......
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Largest Contentful Paint: size when image overflows</title>
<!-- In this test, an image with an intrinsic size of 100 x 50 is added, but
scaled up in order to overflow the viewport. It should not be reported. -->
<body>
<style>
body {
margin: 0px;
}
</style>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/largest-contentful-paint-helpers.js"></script>
<script>
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
async_test(function (t) {
assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
const beforeLoad = performance.now();
new PerformanceObserver(
t.step_func_done(function(entryList) {
assert_equals(entryList.getEntries().length, 1, 'Should have received only one entry!');
const entry = entryList.getEntries()[0];
if (entry.url)
assert_unreached('Should not have received an image entry!');
})
).observe({type: 'largest-contentful-paint', buffered: true});
// Add an image, setting width and height equal to viewport.
img = document.createElement('img');
img.src = '/images/green-100x50.png';
img.id = 'image_id';
img.width = viewportWidth * 2;
img.height = viewportHeight * 2;
img.onload = () => {
const p = document.createElement('p');
p.innerHTML = 'a';
p.style = 'position: absolute; top: 10px; left: 10px;';
document.body.appendChild(p);
}
document.body.appendChild(img);
}, 'The intersectionRect of an img element overflowing is computed correctly');
</script>
</body>
...@@ -6,9 +6,7 @@ ...@@ -6,9 +6,7 @@
<script src="resources/largest-contentful-paint-helpers.js"></script> <script src="resources/largest-contentful-paint-helpers.js"></script>
<script> <script>
async_test(function (t) { async_test(function (t) {
if (!window.PerformanceElementTiming) { assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
assert_unreached("PerformanceElementTiming is not implemented");
}
const beforeLoad = performance.now(); const beforeLoad = performance.now();
new PerformanceObserver( new PerformanceObserver(
t.step_func_done(entryList => { t.step_func_done(entryList => {
......
<!DOCTYPE HTML> <!DOCTYPE HTML>
<meta charset=utf-8> <meta charset=utf-8>
<title>Largest Contentful Paint: size when image overflows</title> <title>Largest Contentful Paint: size when image overflows</title>
<!-- In this test, an image with an intrinsic size of 100 x 50 is added, but
scaled up in order to overflow the viewport. -->
<body> <body>
<style> <style>
body { body {
...@@ -19,10 +17,10 @@ body { ...@@ -19,10 +17,10 @@ body {
let beforeRender; let beforeRender;
const viewportWidth = document.documentElement.clientWidth; const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight; const viewportHeight = document.documentElement.clientHeight;
const imgWidth = 100;
const imgHeight = 50;
async_test(function (t) { async_test(function (t) {
if (!window.PerformanceElementTiming) { assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
assert_unreached("PerformanceElementTiming is not implemented");
}
const beforeLoad = performance.now(); const beforeLoad = performance.now();
new PerformanceObserver( new PerformanceObserver(
t.step_func_done(function(entryList) { t.step_func_done(function(entryList) {
...@@ -30,13 +28,13 @@ body { ...@@ -30,13 +28,13 @@ body {
const entry = entryList.getEntries()[0]; const entry = entryList.getEntries()[0];
const url = window.location.origin + '/images/green-100x50.png'; const url = window.location.origin + '/images/green-100x50.png';
// To compute the size, compute the percentage of the image visible and // To compute the size, compute the percentage of the image visible and
// scale by its natural dimensions. In this test, the image is twice // scale by its natural dimensions. In this test, the image is expanded to twice its size
// the viewport width and twice the viewport height, so it is // but place towards the bottom right corner of the viewport, so it is
// effectively clipped to 50% by 50% of its display size. Scaling by // effectively clipped to 50% by 50% of its display size. Scaling by
// its natural width and height of 100px and 50px respectively, leads // its natural width and height of 100px and 50px respectively, leads
// to a weighted size of 50 by 25. // to a weighted size of 50 by 25.
const truncatedWidth = 100 / 2; const truncatedWidth = imgWidth / 2;
const truncatedHeight = 50 / 2; const truncatedHeight = imgHeight / 2;
const weightedSize = truncatedWidth * truncatedHeight; const weightedSize = truncatedWidth * truncatedHeight;
checkImage(entry, url, 'image_id', weightedSize, beforeLoad); checkImage(entry, url, 'image_id', weightedSize, beforeLoad);
}) })
...@@ -45,8 +43,11 @@ body { ...@@ -45,8 +43,11 @@ body {
img = document.createElement('img'); img = document.createElement('img');
img.src = '/images/green-100x50.png'; img.src = '/images/green-100x50.png';
img.id = 'image_id'; img.id = 'image_id';
img.width = viewportWidth * 2; img.width = imgWidth * 2;
img.height = viewportHeight * 2; img.height = imgHeight * 2;
img.style.position = 'absolute';
img.style.left = viewportWidth - imgWidth + 'px';
img.style.top = viewportHeight - imgHeight + 'px';
document.body.appendChild(img); document.body.appendChild(img);
}, 'The intersectionRect of an img element overflowing is computed correctly'); }, 'The intersectionRect of an img element overflowing is computed correctly');
</script> </script>
......
...@@ -34,7 +34,7 @@ p { ...@@ -34,7 +34,7 @@ p {
); );
observer.observe({type: 'largest-contentful-paint', buffered: true}); observer.observe({type: 'largest-contentful-paint', buffered: true});
beforeRender = performance.now(); beforeRender = performance.now();
}, 'Element with elementtiming attribute is observable.'); }, 'Text element is observable as a LargestContentfulPaint candidate.');
</script> </script>
<p id='my_text'>This is important text! :)</p> <p id='my_text'>This is important text! :)</p>
......
// Receives an image PerformanceElementTiming |entry| and checks |entry|'s attribute values. // Receives an image LargestContentfulPaint |entry| and checks |entry|'s attribute values.
// The |timeLowerBound| parameter is a lower bound on the loadTime value of the entry. // The |timeLowerBound| parameter is a lower bound on the loadTime value of the entry.
// The |options| parameter may contain some string values specifying the following: // The |options| parameter may contain some string values specifying the following:
// * 'renderTimeIs0': the renderTime should be 0 (image does not pass Timing-Allow-Origin checks). // * 'renderTimeIs0': the renderTime should be 0 (image does not pass Timing-Allow-Origin checks).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment