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

[FCP++] Enable subframes, OOPIFs from the renderer side

Currently, FCP++ is only collecting results from the main frame. This CL
will enable it to collect results from subframes, including OOPIFs in
particular.

This CL also simplify the size calculation. Originally, we intersect the
mapped visual rect with the viewport rect. This step is actually
unnecessary, because LocalToAncestorVisualRect() has already included the
viewport clipping.

For OOPIF, this CL adds an extra step upon the normal work flow. Visual
rects of OOPIF live in the main frame space, but they are not clipped by
the main frame viewport. So we add MapToVisualRectInTopFrameSpace, which
do the viewport clipping.

Bug: 934318

Change-Id: I5f91815cafdadd68a01bc1cdd5d8cf009a6e4fbe
Reviewed-on: https://chromium-review.googlesource.com/c/1481574
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarLiquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636902}
parent b4d125cc
...@@ -2712,11 +2712,10 @@ void LocalFrameView::PaintTree() { ...@@ -2712,11 +2712,10 @@ void LocalFrameView::PaintTree() {
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kPaintClean); frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kPaintClean);
if (auto* layout_view = frame_view.GetLayoutView()) if (auto* layout_view = frame_view.GetLayoutView())
layout_view->Layer()->ClearNeedsRepaintRecursively(); layout_view->Layer()->ClearNeedsRepaintRecursively();
if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled())
frame_view.GetPaintTimingDetector().NotifyPaintFinished();
}); });
if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled())
GetPaintTimingDetector().NotifyPaintFinished();
PaintController::ReportUMACounts(); PaintController::ReportUMACounts();
} }
......
...@@ -133,6 +133,9 @@ void ImagePaintTimingDetector::PopulateTraceValue( ...@@ -133,6 +133,9 @@ void ImagePaintTimingDetector::PopulateTraceValue(
value.SetInteger("candidateIndex", candidate_index); value.SetInteger("candidateIndex", candidate_index);
value.SetString("frame", value.SetString("frame",
IdentifiersFactory::FrameId(&frame_view_->GetFrame())); IdentifiersFactory::FrameId(&frame_view_->GetFrame()));
value.SetBoolean("isMainFrame", frame_view_->GetFrame().IsMainFrame());
value.SetBoolean("isOOPIF",
!frame_view_->GetFrame().LocalFrameRoot().IsMainFrame());
} }
void ImagePaintTimingDetector::OnLargestImagePaintDetected( void ImagePaintTimingDetector::OnLargestImagePaintDetected(
......
...@@ -78,6 +78,8 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -78,6 +78,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
static bool IsBackgroundImageContentful(const LayoutObject&, const Image&); static bool IsBackgroundImageContentful(const LayoutObject&, const Image&);
static bool HasBackgroundImage(const LayoutObject& object); static bool HasBackgroundImage(const LayoutObject& object);
void OnPaintFinished(); void OnPaintFinished();
ImageRecord* FindLargestPaintCandidate();
ImageRecord* FindLastPaintCandidate();
void NotifyNodeRemoved(DOMNodeId); void NotifyNodeRemoved(DOMNodeId);
base::TimeTicks LargestImagePaint() const { base::TimeTicks LargestImagePaint() const {
return !largest_image_paint_ return !largest_image_paint_
...@@ -105,8 +107,6 @@ class CORE_EXPORT ImagePaintTimingDetector final ...@@ -105,8 +107,6 @@ class CORE_EXPORT ImagePaintTimingDetector final
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
private: private:
ImageRecord* FindLargestPaintCandidate();
ImageRecord* FindLastPaintCandidate();
ImageRecord* FindCandidate(ImageRecordSet&); ImageRecord* FindCandidate(ImageRecordSet&);
void PopulateTraceValue(TracedValue&, void PopulateTraceValue(TracedValue&,
const ImageRecord& first_image_paint, const ImageRecord& first_image_paint,
......
...@@ -29,7 +29,8 @@ class ImagePaintTimingDetectorTest ...@@ -29,7 +29,8 @@ class ImagePaintTimingDetectorTest
public: public:
ImagePaintTimingDetectorTest() ImagePaintTimingDetectorTest()
: ScopedFirstContentfulPaintPlusPlusForTest(true), : RenderingTest(SingleChildLocalFrameClient::Create()),
ScopedFirstContentfulPaintPlusPlusForTest(true),
base_url_("http://www.test.com/") {} base_url_("http://www.test.com/") {}
~ImagePaintTimingDetectorTest() override { ~ImagePaintTimingDetectorTest() override {
...@@ -41,11 +42,7 @@ class ImagePaintTimingDetectorTest ...@@ -41,11 +42,7 @@ class ImagePaintTimingDetectorTest
void SetUp() override { void SetUp() override {
RenderingTest::SetUp(); RenderingTest::SetUp();
RenderingTest::EnableCompositing(); RenderingTest::EnableCompositing();
GetPaintTimingDetector() ReplaceCallBackQueue(GetPaintTimingDetector());
.GetImagePaintTimingDetector()
.notify_swap_time_override_for_testing_ =
base::BindRepeating(&ImagePaintTimingDetectorTest::FakeNotifySwapTime,
base::Unretained(this));
} }
protected: protected:
...@@ -53,6 +50,24 @@ class ImagePaintTimingDetectorTest ...@@ -53,6 +50,24 @@ class ImagePaintTimingDetectorTest
PaintTimingDetector& GetPaintTimingDetector() { PaintTimingDetector& GetPaintTimingDetector() {
return GetFrameView().GetPaintTimingDetector(); return GetFrameView().GetPaintTimingDetector();
} }
PaintTimingDetector& GetChildPaintTimingDetector() {
return GetChildFrameView().GetPaintTimingDetector();
}
IntRect GetViewportRect(LocalFrameView& view) {
ScrollableArea* scrollable_area = view.GetScrollableArea();
DCHECK(scrollable_area);
return scrollable_area->VisibleContentRect();
}
LocalFrameView& GetChildFrameView() { return *ChildFrame().View(); }
void ReplaceCallBackQueue(PaintTimingDetector& detector) {
detector.GetImagePaintTimingDetector()
.notify_swap_time_override_for_testing_ =
base::BindRepeating(&ImagePaintTimingDetectorTest::FakeNotifySwapTime,
base::Unretained(this));
}
ImageRecord* FindLargestPaintCandidate() { ImageRecord* FindLargestPaintCandidate() {
return GetPaintTimingDetector() return GetPaintTimingDetector()
.GetImagePaintTimingDetector() .GetImagePaintTimingDetector()
...@@ -71,6 +86,12 @@ class ImagePaintTimingDetectorTest ...@@ -71,6 +86,12 @@ class ImagePaintTimingDetectorTest
.id_record_map_.size(); .id_record_map_.size();
} }
unsigned CountChildFrameRecords() {
return GetChildPaintTimingDetector()
.GetImagePaintTimingDetector()
.id_record_map_.size();
}
void Analyze() { void Analyze() {
return GetPaintTimingDetector().GetImagePaintTimingDetector().Analyze(); return GetPaintTimingDetector().GetImagePaintTimingDetector().Analyze();
} }
...@@ -110,8 +131,18 @@ class ImagePaintTimingDetectorTest ...@@ -110,8 +131,18 @@ class ImagePaintTimingDetectorTest
ToHTMLImageElement(element)->SetImageForTest(content); ToHTMLImageElement(element)->SetImageForTest(content);
} }
void SetChildFrameImageAndPaint(AtomicString id, int width, int height) {
DCHECK(ChildFrame().GetDocument());
Element* element = ChildFrame().GetDocument()->getElementById(id);
DCHECK(element);
// Set image and make it loaded.
ImageResourceContent* content = CreateImageForTest(width, height);
ToHTMLImageElement(element)->SetImageForTest(content);
}
void SetVideoImageAndPaint(AtomicString id, int width, int height) { void SetVideoImageAndPaint(AtomicString id, int width, int height) {
Element* element = GetDocument().getElementById(id); Element* element = GetDocument().getElementById(id);
DCHECK(element);
// Set image and make it loaded. // Set image and make it loaded.
ImageResourceContent* content = CreateImageForTest(width, height); ImageResourceContent* content = CreateImageForTest(width, height);
ToHTMLVideoElement(element)->SetImageForTest(content); ToHTMLVideoElement(element)->SetImageForTest(content);
...@@ -656,4 +687,72 @@ TEST_F(ImagePaintTimingDetectorTest, NullTimeNoCrash) { ...@@ -656,4 +687,72 @@ TEST_F(ImagePaintTimingDetectorTest, NullTimeNoCrash) {
Analyze(); Analyze();
} }
TEST_F(ImagePaintTimingDetectorTest, Iframe) {
SetBodyInnerHTML(R"HTML(
<iframe width=100px height=100px></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<style>img { display:block }</style>
<img id="target"></img>
)HTML");
ReplaceCallBackQueue(GetChildPaintTimingDetector());
SetChildFrameImageAndPaint("target", 5, 5);
UpdateAllLifecyclePhasesForTest();
// Ensure main frame doesn't capture this image.
EXPECT_EQ(CountRecords(), 0u);
EXPECT_EQ(CountChildFrameRecords(), 1u);
InvokeCallback();
ImageRecord* image = GetChildFrameView()
.GetPaintTimingDetector()
.GetImagePaintTimingDetector()
.FindLargestPaintCandidate();
EXPECT_TRUE(image);
// Ensure the image size is not clipped (5*5).
EXPECT_EQ(image->first_size, 25ul);
}
TEST_F(ImagePaintTimingDetectorTest, Iframe_ClippedByMainFrameViewport) {
SetBodyInnerHTML(R"HTML(
<style>
#f { margin-top: 1234567px }
</style>
<iframe id="f" width=100px height=100px></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<style>img { display:block }</style>
<img id="target"></img>
)HTML");
// Make sure the iframe is out of main-frame's viewport.
DCHECK_LT(GetViewportRect(GetFrameView()).Height(), 1234567);
ReplaceCallBackQueue(GetChildPaintTimingDetector());
SetChildFrameImageAndPaint("target", 5, 5);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(CountRecords(), 0u);
}
TEST_F(ImagePaintTimingDetectorTest, Iframe_HalfClippedByMainFrameViewport) {
SetBodyInnerHTML(R"HTML(
<style>
#f { margin-left: -5px; }
</style>
<iframe id="f" width=10px height=10px></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<style>img { display:block }</style>
<img id="target"></img>
)HTML");
ReplaceCallBackQueue(GetChildPaintTimingDetector());
SetChildFrameImageAndPaint("target", 10, 10);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(CountRecords(), 0u);
EXPECT_EQ(CountChildFrameRecords(), 1u);
InvokeCallback();
ImageRecord* image = GetChildFrameView()
.GetPaintTimingDetector()
.GetImagePaintTimingDetector()
.FindLargestPaintCandidate();
EXPECT_TRUE(image);
EXPECT_LT(image->first_size, 100ul);
}
} // namespace blink } // namespace blink
...@@ -53,7 +53,7 @@ void PaintTimingDetector::NotifyBackgroundImagePaint( ...@@ -53,7 +53,7 @@ void PaintTimingDetector::NotifyBackgroundImagePaint(
if (!ImagePaintTimingDetector::HasBackgroundImage(*object)) if (!ImagePaintTimingDetector::HasBackgroundImage(*object))
return; return;
LocalFrameView* frame_view = object->GetFrameView(); LocalFrameView* frame_view = object->GetFrameView();
if (!frame_view || !frame_view->GetFrame().IsMainFrame()) if (!frame_view)
return; return;
PaintTimingDetector& detector = frame_view->GetPaintTimingDetector(); PaintTimingDetector& detector = frame_view->GetPaintTimingDetector();
detector.GetImagePaintTimingDetector().RecordImage( detector.GetImagePaintTimingDetector().RecordImage(
...@@ -77,7 +77,7 @@ void PaintTimingDetector::NotifyImagePaint( ...@@ -77,7 +77,7 @@ void PaintTimingDetector::NotifyImagePaint(
const LayoutObject& object, const LayoutObject& object,
const PropertyTreeState& current_paint_chunk_properties) { const PropertyTreeState& current_paint_chunk_properties) {
LocalFrameView* frame_view = object.GetFrameView(); LocalFrameView* frame_view = object.GetFrameView();
if (!frame_view || !frame_view->GetFrame().IsMainFrame()) if (!frame_view)
return; return;
PaintTimingDetector& detector = frame_view->GetPaintTimingDetector(); PaintTimingDetector& detector = frame_view->GetPaintTimingDetector();
detector.GetImagePaintTimingDetector().RecordImage( detector.GetImagePaintTimingDetector().RecordImage(
...@@ -101,7 +101,7 @@ void PaintTimingDetector::NotifyTextPaint( ...@@ -101,7 +101,7 @@ void PaintTimingDetector::NotifyTextPaint(
const LayoutObject& object, const LayoutObject& object,
const PropertyTreeState& current_paint_chunk_properties) { const PropertyTreeState& current_paint_chunk_properties) {
LocalFrameView* frame_view = object.GetFrameView(); LocalFrameView* frame_view = object.GetFrameView();
if (!frame_view || !frame_view->GetFrame().IsMainFrame()) if (!frame_view)
return; return;
PaintTimingDetector& detector = frame_view->GetPaintTimingDetector(); PaintTimingDetector& detector = frame_view->GetPaintTimingDetector();
detector.GetTextPaintTimingDetector().RecordText( detector.GetTextPaintTimingDetector().RecordText(
...@@ -169,16 +169,18 @@ uint64_t PaintTimingDetector::CalculateVisualSize( ...@@ -169,16 +169,18 @@ uint64_t PaintTimingDetector::CalculateVisualSize(
GeometryMapper::LocalToAncestorVisualRect( GeometryMapper::LocalToAncestorVisualRect(
current_paint_chunk_properties, PropertyTreeState::Root(), visual_rect); current_paint_chunk_properties, PropertyTreeState::Root(), visual_rect);
FloatRect& visual_rect_float = visual_rect.Rect(); FloatRect& visual_rect_float = visual_rect.Rect();
if (frame_view_->GetFrame().LocalFrameRoot().IsMainFrame())
// A visual rect means the part of the rect that's visible within return visual_rect_float.Size().Area();
// the viewport. We define the size of it as visual size. // OOPIF. The final rect lives in the iframe's root frame space. We need to
ScrollableArea* scrollable_area = frame_view_->GetScrollableArea(); // project it to the top frame space.
DCHECK(scrollable_area); LayoutRect layout_visual_rect(visual_rect_float);
IntRect viewport = scrollable_area->VisibleContentRect(); frame_view_->GetFrame()
// Use saturated rect to avoid integer-overflow. .LocalFrameRoot()
.View()
visual_rect_float.Intersect(SaturatedRect(viewport)); ->MapToVisualRectInTopFrameSpace(layout_visual_rect);
return visual_rect_float.Size().Area(); return (layout_visual_rect.Size().Width() *
layout_visual_rect.Size().Height())
.ToUnsigned();
} }
void PaintTimingDetector::Dispose() { void PaintTimingDetector::Dispose() {
......
...@@ -54,6 +54,9 @@ void TextPaintTimingDetector::PopulateTraceValue( ...@@ -54,6 +54,9 @@ void TextPaintTimingDetector::PopulateTraceValue(
value.SetInteger("candidateIndex", candidate_index); value.SetInteger("candidateIndex", candidate_index);
value.SetString("frame", value.SetString("frame",
IdentifiersFactory::FrameId(&frame_view_->GetFrame())); IdentifiersFactory::FrameId(&frame_view_->GetFrame()));
value.SetBoolean("isMainFrame", frame_view_->GetFrame().IsMainFrame());
value.SetBoolean("isOOPIF",
!frame_view_->GetFrame().LocalFrameRoot().IsMainFrame());
} }
void TextPaintTimingDetector::OnLargestTextDetected( void TextPaintTimingDetector::OnLargestTextDetected(
......
...@@ -16,7 +16,8 @@ class TextPaintTimingDetectorTest ...@@ -16,7 +16,8 @@ class TextPaintTimingDetectorTest
private ScopedFirstContentfulPaintPlusPlusForTest { private ScopedFirstContentfulPaintPlusPlusForTest {
public: public:
TextPaintTimingDetectorTest() TextPaintTimingDetectorTest()
: ScopedFirstContentfulPaintPlusPlusForTest(true) {} : RenderingTest(SingleChildLocalFrameClient::Create()),
ScopedFirstContentfulPaintPlusPlusForTest(true) {}
void SetUp() override { void SetUp() override {
RenderingTest::SetUp(); RenderingTest::SetUp();
RenderingTest::EnableCompositing(); RenderingTest::EnableCompositing();
...@@ -28,6 +29,14 @@ class TextPaintTimingDetectorTest ...@@ -28,6 +29,14 @@ class TextPaintTimingDetectorTest
return GetFrameView().GetPaintTimingDetector(); return GetFrameView().GetPaintTimingDetector();
} }
IntRect GetViewportRect(LocalFrameView& view) {
ScrollableArea* scrollable_area = view.GetScrollableArea();
DCHECK(scrollable_area);
return scrollable_area->VisibleContentRect();
}
LocalFrameView& GetChildFrameView() { return *ChildFrame().View(); }
unsigned CountVisibleTexts() { unsigned CountVisibleTexts() {
return GetPaintTimingDetector() return GetPaintTimingDetector()
.GetTextPaintTimingDetector() .GetTextPaintTimingDetector()
...@@ -35,7 +44,6 @@ class TextPaintTimingDetectorTest ...@@ -35,7 +44,6 @@ class TextPaintTimingDetectorTest
GetPaintTimingDetector() GetPaintTimingDetector()
.GetTextPaintTimingDetector() .GetTextPaintTimingDetector()
.detached_ids_.size(); .detached_ids_.size();
;
} }
unsigned CountDetachedTexts() { unsigned CountDetachedTexts() {
...@@ -63,9 +71,9 @@ class TextPaintTimingDetectorTest ...@@ -63,9 +71,9 @@ class TextPaintTimingDetectorTest
.last_text_paint_; .last_text_paint_;
} }
// This only triggers ReportSwapTime in main frame.
void UpdateAllLifecyclePhasesAndSimulateSwapTime() { void UpdateAllLifecyclePhasesAndSimulateSwapTime() {
GetFrameView().UpdateAllLifecyclePhases( UpdateAllLifecyclePhasesForTest();
DocumentLifecycle::LifecycleUpdateReason::kTest);
TextPaintTimingDetector& detector = TextPaintTimingDetector& detector =
GetPaintTimingDetector().GetTextPaintTimingDetector(); GetPaintTimingDetector().GetTextPaintTimingDetector();
if (!detector.texts_to_record_swap_time_.empty()) { if (!detector.texts_to_record_swap_time_.empty()) {
...@@ -74,6 +82,20 @@ class TextPaintTimingDetectorTest ...@@ -74,6 +82,20 @@ class TextPaintTimingDetectorTest
} }
} }
size_t CountPendingSwapTime(LocalFrameView& frame_view) {
TextPaintTimingDetector& detector =
frame_view.GetPaintTimingDetector().GetTextPaintTimingDetector();
return detector.texts_to_record_swap_time_.size();
}
void ChildFrameSwapTimeCallBack() {
GetChildFrameView()
.GetPaintTimingDetector()
.GetTextPaintTimingDetector()
.ReportSwapTime(WebLayerTreeView::SwapResult::kDidSwap,
CurrentTimeTicks());
}
void SimulateAnalyze() { void SimulateAnalyze() {
GetPaintTimingDetector().GetTextPaintTimingDetector().Analyze(); GetPaintTimingDetector().GetTextPaintTimingDetector().Analyze();
} }
...@@ -500,4 +522,56 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_ReportLastNullCandidate) { ...@@ -500,4 +522,56 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_ReportLastNullCandidate) {
EXPECT_EQ(LastPaintStoredResult(), base::TimeTicks()); EXPECT_EQ(LastPaintStoredResult(), base::TimeTicks());
} }
// This is for comparison with the ClippedByViewport test.
TEST_F(TextPaintTimingDetectorTest, NormalTextUnclipped) {
SetBodyInnerHTML(R"HTML(
<div id='d'>text</div>
)HTML");
EXPECT_EQ(CountPendingSwapTime(GetFrameView()), 1u);
EXPECT_EQ(CountVisibleTexts(), 1u);
}
TEST_F(TextPaintTimingDetectorTest, ClippedByViewport) {
SetBodyInnerHTML(R"HTML(
<style>
#d { margin-top: 1234567px }
</style>
<div id='d'>text</div>
)HTML");
// Make sure the margin-top is larger than the viewport height.
DCHECK_LT(GetViewportRect(GetFrameView()).Height(), 1234567);
EXPECT_EQ(CountPendingSwapTime(GetFrameView()), 0u);
EXPECT_EQ(CountVisibleTexts(), 0u);
}
TEST_F(TextPaintTimingDetectorTest, Iframe) {
SetBodyInnerHTML(R"HTML(
<iframe width=100px height=100px></iframe>
)HTML");
SetChildFrameHTML("A");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(CountPendingSwapTime(GetChildFrameView()), 1u);
ChildFrameSwapTimeCallBack();
TextRecord* text = GetChildFrameView()
.GetPaintTimingDetector()
.GetTextPaintTimingDetector()
.FindLargestPaintCandidate();
EXPECT_TRUE(text);
}
TEST_F(TextPaintTimingDetectorTest, Iframe_ClippedByViewport) {
SetBodyInnerHTML(R"HTML(
<iframe width=100px height=100px></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<style>
#d { margin-top: 200px }
</style>
<div id='d'>text</div>
)HTML");
DCHECK_EQ(GetViewportRect(GetChildFrameView()).Height(), 100);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(CountPendingSwapTime(GetChildFrameView()), 0u);
}
} // 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