Commit 3039f172 authored by Xida Chen's avatar Xida Chen Committed by Commit Bot

Detect canvas invalidation during document lifecycle update

As discussed in this doc:
https://docs.google.com/document/d/1zCIs8zBE7SPIS_OTvTuLkLbEgSfh-RxF0cYSCNAm-Ko/edit?ts=5f4ea439#heading=h.7o3hpjjptc4v

We would apply some heuristic to detect js-driven animation, and
canvas invalidation is one of them. This CL detects that, it contains
a unit test to verify the correctness.

Bug: 1111392
Change-Id: I7f37077b1a0da3c85301bf6034fde96898a84534
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2393068
Commit-Queue: Xida Chen <xidachen@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808141}
parent 99559af3
......@@ -127,6 +127,12 @@ void AnimationHost::RemoveAnimationTimeline(
SetNeedsPushProperties();
}
void AnimationHost::SetHasCanvasInvalidation(bool has_canvas_invalidation) {
// TODO(crbug.com/1111392): send this value to LayerTreeHostImpl for
// constructing a canvas frame sequence tracker.
has_canvas_invalidation_ = has_canvas_invalidation;
}
void AnimationHost::UpdateRegisteredElementIds(ElementListType changed_list) {
for (auto map_entry : element_to_animations_map_) {
if (mutator_host_client()->IsElementInPropertyTrees(map_entry.first,
......
......@@ -221,6 +221,8 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost,
bool current_frame_had_raf,
bool next_frame_has_pending_raf);
void SetHasCanvasInvalidation(bool has_canvas_invalidation);
private:
explicit AnimationHost(ThreadInstance thread_instance);
......@@ -272,6 +274,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost,
size_t main_thread_animations_count_ = 0;
bool current_frame_had_raf_ = false;
bool next_frame_has_pending_raf_ = false;
bool has_canvas_invalidation_ = false;
PendingThroughputTrackerInfos pending_throughput_tracker_infos_;
......
......@@ -2797,6 +2797,8 @@ void LocalFrameView::RunPaintLifecyclePhase() {
}
}
}
if (GetPage())
GetPage()->Animator().ReportFrameAnimations(GetCompositorAnimationHost());
}
bool LocalFrameView::RunAccessibilityLifecyclePhase(
......
......@@ -471,6 +471,9 @@ bool HTMLCanvasElement::IsWebGLBlocked() const {
void HTMLCanvasElement::DidDraw(const FloatRect& rect) {
if (rect.IsEmpty())
return;
if (GetLayoutObject() && GetLayoutObject()->PreviousVisibilityVisible() &&
GetDocument().GetPage())
GetDocument().GetPage()->Animator().SetHasCanvasInvalidation(true);
canvas_is_clear_ = false;
if (GetLayoutObject() && !LowLatencyEnabled())
GetLayoutObject()->SetShouldCheckForPaintInvalidation();
......
......@@ -9,7 +9,11 @@
namespace blink {
using HTMLCanvasElementTest = RenderingTest;
class HTMLCanvasElementTest : public RenderingTest {
public:
HTMLCanvasElementTest()
: RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
};
TEST_F(HTMLCanvasElementTest, CreateLayerUpdatesCompositing) {
// Enable script so that the canvas will create a LayoutHTMLCanvas.
......@@ -29,4 +33,86 @@ TEST_F(HTMLCanvasElementTest, CreateLayerUpdatesCompositing) {
EXPECT_EQ(CompositingReason::kCanvas, layer->DirectCompositingReasons());
}
TEST_F(HTMLCanvasElementTest, CanvasInvalidation) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<canvas id='canvas' width='10px' height='10px'></canvas>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
GetDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
RunDocumentLifecycle();
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
TEST_F(HTMLCanvasElementTest, CanvasNotInvalidatedOnFirstFrameInDOM) {
GetDocument().GetSettings()->SetScriptEnabled(true);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
TEST_F(HTMLCanvasElementTest, CanvasNotInvalidatedOnFirstPaint) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<canvas id='canvas' style='display:none'></canvas>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
RunDocumentLifecycle();
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.getElementById('canvas');
canvas.style.display = 'block';
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
TEST_F(HTMLCanvasElementTest, CanvasInvalidationInFrame) {
SetBodyInnerHTML(R"HTML(
<iframe id='iframe'></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<canvas id='canvas' width='10px' height='10px'></canvas>
)HTML");
GetDocument().GetSettings()->SetScriptEnabled(true);
ChildDocument().GetSettings()->SetScriptEnabled(true);
EXPECT_FALSE(
ChildDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
RunDocumentLifecycle();
auto* script = ChildDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
ChildDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
} // namespace blink
......@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/page/page_animator.h"
#include "base/auto_reset.h"
#include "cc/animation/animation_host.h"
#include "third_party/blink/renderer/core/animation/document_animations.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
......@@ -120,6 +121,16 @@ void PageAnimator::PostAnimate() {
Clock().SetAllowedToDynamicallyUpdateTime(true);
}
void PageAnimator::SetHasCanvasInvalidation(bool has_canvas_invalidation) {
has_canvas_invalidation_ = has_canvas_invalidation;
}
void PageAnimator::ReportFrameAnimations(cc::AnimationHost* animation_host) {
if (animation_host)
animation_host->SetHasCanvasInvalidation(has_canvas_invalidation_);
has_canvas_invalidation_ = false;
}
void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only(
bool suppress_frame_requests) {
// If we are enabling the suppression and it was already enabled then we must
......
......@@ -12,6 +12,10 @@
#include "third_party/blink/renderer/core/dom/document_lifecycle.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace cc {
class AnimationHost;
}
namespace blink {
class LocalFrame;
......@@ -45,6 +49,11 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
DocumentUpdateReason reason);
AnimationClock& Clock() { return animation_clock_; }
HeapVector<Member<Animation>> GetAnimations(const TreeScope&);
void SetHasCanvasInvalidation(bool has_canvas_invalidation);
bool has_canvas_invalidation_for_test() const {
return has_canvas_invalidation_;
}
void ReportFrameAnimations(cc::AnimationHost* animation_host);
private:
Member<Page> page_;
......@@ -52,6 +61,8 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
bool updating_layout_and_style_for_painting_;
bool suppress_frame_requests_workaround_for704763_only_ = false;
AnimationClock animation_clock_;
// True if the current main frame has canvas invalidation.
bool has_canvas_invalidation_ = false;
};
} // 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