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() {
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kPaintClean);
if (auto* layout_view = frame_view.GetLayoutView())
layout_view->Layer()->ClearNeedsRepaintRecursively();
if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled())
frame_view.GetPaintTimingDetector().NotifyPaintFinished();
});
if (RuntimeEnabledFeatures::FirstContentfulPaintPlusPlusEnabled())
GetPaintTimingDetector().NotifyPaintFinished();
PaintController::ReportUMACounts();
}
......
......@@ -133,6 +133,9 @@ void ImagePaintTimingDetector::PopulateTraceValue(
value.SetInteger("candidateIndex", candidate_index);
value.SetString("frame",
IdentifiersFactory::FrameId(&frame_view_->GetFrame()));
value.SetBoolean("isMainFrame", frame_view_->GetFrame().IsMainFrame());
value.SetBoolean("isOOPIF",
!frame_view_->GetFrame().LocalFrameRoot().IsMainFrame());
}
void ImagePaintTimingDetector::OnLargestImagePaintDetected(
......
......@@ -78,6 +78,8 @@ class CORE_EXPORT ImagePaintTimingDetector final
static bool IsBackgroundImageContentful(const LayoutObject&, const Image&);
static bool HasBackgroundImage(const LayoutObject& object);
void OnPaintFinished();
ImageRecord* FindLargestPaintCandidate();
ImageRecord* FindLastPaintCandidate();
void NotifyNodeRemoved(DOMNodeId);
base::TimeTicks LargestImagePaint() const {
return !largest_image_paint_
......@@ -105,8 +107,6 @@ class CORE_EXPORT ImagePaintTimingDetector final
void Trace(blink::Visitor*);
private:
ImageRecord* FindLargestPaintCandidate();
ImageRecord* FindLastPaintCandidate();
ImageRecord* FindCandidate(ImageRecordSet&);
void PopulateTraceValue(TracedValue&,
const ImageRecord& first_image_paint,
......
......@@ -29,7 +29,8 @@ class ImagePaintTimingDetectorTest
public:
ImagePaintTimingDetectorTest()
: ScopedFirstContentfulPaintPlusPlusForTest(true),
: RenderingTest(SingleChildLocalFrameClient::Create()),
ScopedFirstContentfulPaintPlusPlusForTest(true),
base_url_("http://www.test.com/") {}
~ImagePaintTimingDetectorTest() override {
......@@ -41,11 +42,7 @@ class ImagePaintTimingDetectorTest
void SetUp() override {
RenderingTest::SetUp();
RenderingTest::EnableCompositing();
GetPaintTimingDetector()
.GetImagePaintTimingDetector()
.notify_swap_time_override_for_testing_ =
base::BindRepeating(&ImagePaintTimingDetectorTest::FakeNotifySwapTime,
base::Unretained(this));
ReplaceCallBackQueue(GetPaintTimingDetector());
}
protected:
......@@ -53,6 +50,24 @@ class ImagePaintTimingDetectorTest
PaintTimingDetector& 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() {
return GetPaintTimingDetector()
.GetImagePaintTimingDetector()
......@@ -71,6 +86,12 @@ class ImagePaintTimingDetectorTest
.id_record_map_.size();
}
unsigned CountChildFrameRecords() {
return GetChildPaintTimingDetector()
.GetImagePaintTimingDetector()
.id_record_map_.size();
}
void Analyze() {
return GetPaintTimingDetector().GetImagePaintTimingDetector().Analyze();
}
......@@ -110,8 +131,18 @@ class ImagePaintTimingDetectorTest
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) {
Element* element = GetDocument().getElementById(id);
DCHECK(element);
// Set image and make it loaded.
ImageResourceContent* content = CreateImageForTest(width, height);
ToHTMLVideoElement(element)->SetImageForTest(content);
......@@ -656,4 +687,72 @@ TEST_F(ImagePaintTimingDetectorTest, NullTimeNoCrash) {
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
......@@ -53,7 +53,7 @@ void PaintTimingDetector::NotifyBackgroundImagePaint(
if (!ImagePaintTimingDetector::HasBackgroundImage(*object))
return;
LocalFrameView* frame_view = object->GetFrameView();
if (!frame_view || !frame_view->GetFrame().IsMainFrame())
if (!frame_view)
return;
PaintTimingDetector& detector = frame_view->GetPaintTimingDetector();
detector.GetImagePaintTimingDetector().RecordImage(
......@@ -77,7 +77,7 @@ void PaintTimingDetector::NotifyImagePaint(
const LayoutObject& object,
const PropertyTreeState& current_paint_chunk_properties) {
LocalFrameView* frame_view = object.GetFrameView();
if (!frame_view || !frame_view->GetFrame().IsMainFrame())
if (!frame_view)
return;
PaintTimingDetector& detector = frame_view->GetPaintTimingDetector();
detector.GetImagePaintTimingDetector().RecordImage(
......@@ -101,7 +101,7 @@ void PaintTimingDetector::NotifyTextPaint(
const LayoutObject& object,
const PropertyTreeState& current_paint_chunk_properties) {
LocalFrameView* frame_view = object.GetFrameView();
if (!frame_view || !frame_view->GetFrame().IsMainFrame())
if (!frame_view)
return;
PaintTimingDetector& detector = frame_view->GetPaintTimingDetector();
detector.GetTextPaintTimingDetector().RecordText(
......@@ -169,16 +169,18 @@ uint64_t PaintTimingDetector::CalculateVisualSize(
GeometryMapper::LocalToAncestorVisualRect(
current_paint_chunk_properties, PropertyTreeState::Root(), visual_rect);
FloatRect& visual_rect_float = visual_rect.Rect();
// A visual rect means the part of the rect that's visible within
// the viewport. We define the size of it as visual size.
ScrollableArea* scrollable_area = frame_view_->GetScrollableArea();
DCHECK(scrollable_area);
IntRect viewport = scrollable_area->VisibleContentRect();
// Use saturated rect to avoid integer-overflow.
visual_rect_float.Intersect(SaturatedRect(viewport));
return visual_rect_float.Size().Area();
if (frame_view_->GetFrame().LocalFrameRoot().IsMainFrame())
return visual_rect_float.Size().Area();
// OOPIF. The final rect lives in the iframe's root frame space. We need to
// project it to the top frame space.
LayoutRect layout_visual_rect(visual_rect_float);
frame_view_->GetFrame()
.LocalFrameRoot()
.View()
->MapToVisualRectInTopFrameSpace(layout_visual_rect);
return (layout_visual_rect.Size().Width() *
layout_visual_rect.Size().Height())
.ToUnsigned();
}
void PaintTimingDetector::Dispose() {
......
......@@ -54,6 +54,9 @@ void TextPaintTimingDetector::PopulateTraceValue(
value.SetInteger("candidateIndex", candidate_index);
value.SetString("frame",
IdentifiersFactory::FrameId(&frame_view_->GetFrame()));
value.SetBoolean("isMainFrame", frame_view_->GetFrame().IsMainFrame());
value.SetBoolean("isOOPIF",
!frame_view_->GetFrame().LocalFrameRoot().IsMainFrame());
}
void TextPaintTimingDetector::OnLargestTextDetected(
......
......@@ -16,7 +16,8 @@ class TextPaintTimingDetectorTest
private ScopedFirstContentfulPaintPlusPlusForTest {
public:
TextPaintTimingDetectorTest()
: ScopedFirstContentfulPaintPlusPlusForTest(true) {}
: RenderingTest(SingleChildLocalFrameClient::Create()),
ScopedFirstContentfulPaintPlusPlusForTest(true) {}
void SetUp() override {
RenderingTest::SetUp();
RenderingTest::EnableCompositing();
......@@ -28,6 +29,14 @@ class TextPaintTimingDetectorTest
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() {
return GetPaintTimingDetector()
.GetTextPaintTimingDetector()
......@@ -35,7 +44,6 @@ class TextPaintTimingDetectorTest
GetPaintTimingDetector()
.GetTextPaintTimingDetector()
.detached_ids_.size();
;
}
unsigned CountDetachedTexts() {
......@@ -63,9 +71,9 @@ class TextPaintTimingDetectorTest
.last_text_paint_;
}
// This only triggers ReportSwapTime in main frame.
void UpdateAllLifecyclePhasesAndSimulateSwapTime() {
GetFrameView().UpdateAllLifecyclePhases(
DocumentLifecycle::LifecycleUpdateReason::kTest);
UpdateAllLifecyclePhasesForTest();
TextPaintTimingDetector& detector =
GetPaintTimingDetector().GetTextPaintTimingDetector();
if (!detector.texts_to_record_swap_time_.empty()) {
......@@ -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() {
GetPaintTimingDetector().GetTextPaintTimingDetector().Analyze();
}
......@@ -500,4 +522,56 @@ TEST_F(TextPaintTimingDetectorTest, LastTextPaint_ReportLastNullCandidate) {
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
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