Commit ad11b562 authored by Liquan(Max) Gu's avatar Liquan(Max) Gu Committed by Commit Bot

[FCP++] Nodes with the same size shouldn't be ignored

This CL is to fix a bug in FCP++. FCP++ is currently using a set for ranking
the nodes' size. When two node sizes are the same, the set will ignore one of
them. This bug will manifest in the following scenario:
* A and B element have the same size, which is the largest on the page.
* When the set adds A and B, it ignores B implicitly.
* A is detached from the DOM.
* LCP reports a candidate. At this time, LCP should have reported B, but as B
is not in the ranking set, it's ignored by LCP as well.

Bug: 944248
Change-Id: I55d6856ff93191494a0bd46f7043b76c2f674b7d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1534303Reviewed-by: default avatarSteve Kobes <skobes@chromium.org>
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#644802}
parent dab2fd56
...@@ -106,7 +106,11 @@ static bool LargeImageFirst(const base::WeakPtr<ImageRecord>& a, ...@@ -106,7 +106,11 @@ static bool LargeImageFirst(const base::WeakPtr<ImageRecord>& a,
const base::WeakPtr<ImageRecord>& b) { const base::WeakPtr<ImageRecord>& b) {
DCHECK(a); DCHECK(a);
DCHECK(b); DCHECK(b);
return a->first_size > b->first_size; if (a->first_size != b->first_size)
return a->first_size > b->first_size;
// This make sure that two different nodes with the same |first_size| wouldn't
// be merged in the set.
return a->node_id > b->node_id;
} }
ImagePaintTimingDetector::ImagePaintTimingDetector(LocalFrameView* frame_view) ImagePaintTimingDetector::ImagePaintTimingDetector(LocalFrameView* frame_view)
...@@ -355,12 +359,9 @@ void ImagePaintTimingDetector::StopRecordEntries() { ...@@ -355,12 +359,9 @@ void ImagePaintTimingDetector::StopRecordEntries() {
} }
ImageRecord* ImagePaintTimingDetector::FindLargestPaintCandidate() { ImageRecord* ImagePaintTimingDetector::FindLargestPaintCandidate() {
return FindCandidate(size_ordered_set_); DCHECK_EQ(id_record_map_.size(), size_ordered_set_.size());
} for (auto it = size_ordered_set_.begin(); it != size_ordered_set_.end();
++it) {
ImageRecord* ImagePaintTimingDetector::FindCandidate(
ImageRecordSet& ordered_set) {
for (auto it = ordered_set.begin(); it != ordered_set.end(); ++it) {
if (detached_ids_.Contains((*it)->node_id)) if (detached_ids_.Contains((*it)->node_id))
continue; continue;
DCHECK(id_record_map_.Contains((*it)->node_id)); DCHECK(id_record_map_.Contains((*it)->node_id));
......
...@@ -90,7 +90,6 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -90,7 +90,6 @@ class CORE_EXPORT ImagePaintTimingDetector final
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
private: private:
ImageRecord* FindCandidate(ImageRecordSet&);
void PopulateTraceValue(TracedValue&, void PopulateTraceValue(TracedValue&,
const ImageRecord& first_image_paint, const ImageRecord& first_image_paint,
unsigned report_count) const; unsigned report_count) const;
......
...@@ -86,6 +86,12 @@ class ImagePaintTimingDetectorTest ...@@ -86,6 +86,12 @@ class ImagePaintTimingDetectorTest
.id_record_map_.size(); .id_record_map_.size();
} }
size_t CountRankingSetRecords() {
return GetPaintTimingDetector()
.GetImagePaintTimingDetector()
.size_ordered_set_.size();
}
void Analyze() { void Analyze() {
return GetPaintTimingDetector().GetImagePaintTimingDetector().Analyze(); return GetPaintTimingDetector().GetImagePaintTimingDetector().Analyze();
} }
...@@ -214,17 +220,6 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) { ...@@ -214,17 +220,6 @@ TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_Largest) {
SetImageAndPaint("larger", 9, 9); SetImageAndPaint("larger", 9, 9);
UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
// record = FindLargestPaintCandidate();
// EXPECT_TRUE(record);
// EXPECT_EQ(record->first_size, 81ul);
// EXPECT_TRUE(record->loaded);
// SetImageAndPaint("medium", 7, 7);
// UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
// record = FindLargestPaintCandidate();
// EXPECT_TRUE(record);
// EXPECT_EQ(record->first_size, 81ul);
// EXPECT_TRUE(record->loaded);
} }
TEST_F(ImagePaintTimingDetectorTest, TEST_F(ImagePaintTimingDetectorTest,
...@@ -586,4 +581,18 @@ TEST_F(ImagePaintTimingDetectorTest, Iframe_HalfClippedByMainFrameViewport) { ...@@ -586,4 +581,18 @@ TEST_F(ImagePaintTimingDetectorTest, Iframe_HalfClippedByMainFrameViewport) {
EXPECT_LT(image->first_size, 100ul); EXPECT_LT(image->first_size, 100ul);
} }
TEST_F(ImagePaintTimingDetectorTest, SameSizeShouldNotBeIgnored) {
SetBodyInnerHTML(R"HTML(
<style>img { display:block }</style>
<img id='1'></img>
<img id='2'></img>
<img id='3'></img>
)HTML");
SetImageAndPaint("1", 5, 5);
SetImageAndPaint("2", 5, 5);
SetImageAndPaint("3", 5, 5);
UpdateAllLifecyclePhasesAndInvokeCallbackIfAny();
EXPECT_EQ(CountRankingSetRecords(), 3u);
}
} // namespace blink } // namespace blink
...@@ -28,7 +28,13 @@ constexpr size_t kTextNodeNumberLimit = 5000; ...@@ -28,7 +28,13 @@ constexpr size_t kTextNodeNumberLimit = 5000;
static bool LargeTextFirst(const base::WeakPtr<TextRecord>& a, static bool LargeTextFirst(const base::WeakPtr<TextRecord>& a,
const base::WeakPtr<TextRecord>& b) { const base::WeakPtr<TextRecord>& b) {
return a->first_size > b->first_size; DCHECK(a);
DCHECK(b);
if (a->first_size != b->first_size)
return a->first_size > b->first_size;
// This make sure that two different nodes with the same |first_size| wouldn't
// be merged in the set.
return a->node_id > b->node_id;
} }
TextPaintTimingDetector::TextPaintTimingDetector(LocalFrameView* frame_view) TextPaintTimingDetector::TextPaintTimingDetector(LocalFrameView* frame_view)
...@@ -281,7 +287,7 @@ TextRecord* TextRecordsManager::FindLargestPaintCandidate() { ...@@ -281,7 +287,7 @@ TextRecord* TextRecordsManager::FindLargestPaintCandidate() {
// TODO(crbug/944248): An identified bug here is that the records with the // TODO(crbug/944248): An identified bug here is that the records with the
// same size will be silently ignored by |size_ordered_set_|. This is what // same size will be silently ignored by |size_ordered_set_|. This is what
// causes these two sizes to be different. // causes these two sizes to be different.
DCHECK_GE(visible_node_map.size(), size_ordered_set_.size()); DCHECK_EQ(visible_node_map.size(), size_ordered_set_.size());
if (!is_result_invalidated_) if (!is_result_invalidated_)
return cached_largest_paint_candidate_; return cached_largest_paint_candidate_;
TextRecord* new_largest_paint_candidate = nullptr; TextRecord* new_largest_paint_candidate = nullptr;
......
...@@ -46,6 +46,12 @@ class TextPaintTimingDetectorTest ...@@ -46,6 +46,12 @@ class TextPaintTimingDetectorTest
.records_manager_.detached_ids_.size(); .records_manager_.detached_ids_.size();
} }
unsigned CountRankingSetSize() {
return GetPaintTimingDetector()
.GetTextPaintTimingDetector()
.records_manager_.size_ordered_set_.size();
}
unsigned CountDetachedTexts() { unsigned CountDetachedTexts() {
return GetPaintTimingDetector() return GetPaintTimingDetector()
.GetTextPaintTimingDetector() .GetTextPaintTimingDetector()
...@@ -510,4 +516,15 @@ TEST_F(TextPaintTimingDetectorTest, Iframe_ClippedByViewport) { ...@@ -510,4 +516,15 @@ TEST_F(TextPaintTimingDetectorTest, Iframe_ClippedByViewport) {
EXPECT_EQ(CountPendingSwapTime(GetChildFrameView()), 0u); EXPECT_EQ(CountPendingSwapTime(GetChildFrameView()), 0u);
} }
TEST_F(TextPaintTimingDetectorTest, SameSizeShouldNotBeIgnored) {
SetBodyInnerHTML(R"HTML(
<div>text</div>
<div>text</div>
<div>text</div>
<div>text</div>
)HTML");
UpdateAllLifecyclePhasesAndSimulateSwapTime();
EXPECT_EQ(CountRankingSetSize(), 4u);
}
} // namespace blink } // namespace blink
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