Commit 0b89e364 authored by Xida Chen's avatar Xida Chen Committed by Commit Bot

Detect inline style mutation

This is the design doc:
https://docs.google.com/document/d/1zCIs8zBE7SPIS_OTvTuLkLbEgSfh-RxF0cYSCNAm-Ko/edit?ts=5f4ea439#heading=h.7o3hpjjptc4v

In this CL, we detect inline style mutation.

Bug: 1111392
Change-Id: I7ed591ee46429fc59d582dd47119974e9e585ba4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2408152
Commit-Queue: Xida Chen <xidachen@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808547}
parent b8f2d817
...@@ -133,6 +133,12 @@ void AnimationHost::SetHasCanvasInvalidation(bool has_canvas_invalidation) { ...@@ -133,6 +133,12 @@ void AnimationHost::SetHasCanvasInvalidation(bool has_canvas_invalidation) {
has_canvas_invalidation_ = has_canvas_invalidation; has_canvas_invalidation_ = has_canvas_invalidation;
} }
void AnimationHost::SetHasInlineStyleMutation(bool has_inline_style_mutation) {
// TODO(crbug.com/1111392): send this value to LayerTreeHostImpl for
// constructing a tracker.
has_inline_style_mutation_ = has_inline_style_mutation;
}
void AnimationHost::UpdateRegisteredElementIds(ElementListType changed_list) { void AnimationHost::UpdateRegisteredElementIds(ElementListType changed_list) {
for (auto map_entry : element_to_animations_map_) { for (auto map_entry : element_to_animations_map_) {
if (mutator_host_client()->IsElementInPropertyTrees(map_entry.first, if (mutator_host_client()->IsElementInPropertyTrees(map_entry.first,
......
...@@ -222,6 +222,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, ...@@ -222,6 +222,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost,
bool next_frame_has_pending_raf); bool next_frame_has_pending_raf);
void SetHasCanvasInvalidation(bool has_canvas_invalidation); void SetHasCanvasInvalidation(bool has_canvas_invalidation);
void SetHasInlineStyleMutation(bool has_inline_style_mutation);
private: private:
explicit AnimationHost(ThreadInstance thread_instance); explicit AnimationHost(ThreadInstance thread_instance);
...@@ -275,6 +276,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, ...@@ -275,6 +276,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost,
bool current_frame_had_raf_ = false; bool current_frame_had_raf_ = false;
bool next_frame_has_pending_raf_ = false; bool next_frame_has_pending_raf_ = false;
bool has_canvas_invalidation_ = false; bool has_canvas_invalidation_ = false;
bool has_inline_style_mutation_ = false;
PendingThroughputTrackerInfos pending_throughput_tracker_infos_; PendingThroughputTrackerInfos pending_throughput_tracker_infos_;
......
...@@ -33,6 +33,7 @@ const CSSValue* InlineStylePropertyMap::GetCustomProperty( ...@@ -33,6 +33,7 @@ const CSSValue* InlineStylePropertyMap::GetCustomProperty(
void InlineStylePropertyMap::SetProperty(CSSPropertyID property_id, void InlineStylePropertyMap::SetProperty(CSSPropertyID property_id,
const CSSValue& value) { const CSSValue& value) {
owner_element_->SetInlineStyleProperty(property_id, value); owner_element_->SetInlineStyleProperty(property_id, value);
owner_element_->NotifyInlineStyleMutation();
} }
bool InlineStylePropertyMap::SetShorthandProperty( bool InlineStylePropertyMap::SetShorthandProperty(
...@@ -55,6 +56,7 @@ void InlineStylePropertyMap::SetCustomProperty( ...@@ -55,6 +56,7 @@ void InlineStylePropertyMap::SetCustomProperty(
CSSPropertyID::kVariable, CSSPropertyID::kVariable,
*MakeGarbageCollected<CSSCustomPropertyDeclaration>(property_name, *MakeGarbageCollected<CSSCustomPropertyDeclaration>(property_name,
variable_data)); variable_data));
owner_element_->NotifyInlineStyleMutation();
} }
void InlineStylePropertyMap::RemoveProperty(CSSPropertyID property_id) { void InlineStylePropertyMap::RemoveProperty(CSSPropertyID property_id) {
......
...@@ -40,6 +40,7 @@ void InlineCSSStyleDeclaration::DidMutate(MutationType type) { ...@@ -40,6 +40,7 @@ void InlineCSSStyleDeclaration::DidMutate(MutationType type) {
if (!parent_element_) if (!parent_element_)
return; return;
parent_element_->NotifyInlineStyleMutation();
parent_element_->ClearMutableInlineStyleIfEmpty(); parent_element_->ClearMutableInlineStyleIfEmpty();
parent_element_->InvalidateStyleAttribute(); parent_element_->InvalidateStyleAttribute();
StyleAttributeMutationScope(this).DidInvalidateStyleAttr(); StyleAttributeMutationScope(this).DidInvalidateStyleAttr();
......
...@@ -5917,6 +5917,13 @@ void Element::ClearMutableInlineStyleIfEmpty() { ...@@ -5917,6 +5917,13 @@ void Element::ClearMutableInlineStyleIfEmpty() {
} }
} }
void Element::NotifyInlineStyleMutation() {
if (GetLayoutObject() && GetLayoutObject()->PreviousVisibilityVisible() &&
GetDocument().GetPage()) {
GetDocument().GetPage()->Animator().SetHasInlineStyleMutation();
}
}
inline void Element::SetInlineStyleFromString( inline void Element::SetInlineStyleFromString(
const AtomicString& new_style_string) { const AtomicString& new_style_string) {
DCHECK(IsStyledElement()); DCHECK(IsStyledElement());
......
...@@ -931,6 +931,8 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable { ...@@ -931,6 +931,8 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable {
// been determined to be from an ad. Returns false by default. // been determined to be from an ad. Returns false by default.
virtual bool IsAdRelated() const { return false; } virtual bool IsAdRelated() const { return false; }
void NotifyInlineStyleMutation();
protected: protected:
const ElementData* GetElementData() const { return element_data_.Get(); } const ElementData* GetElementData() const { return element_data_.Get(); }
UniqueElementData& EnsureUniqueElementData(); UniqueElementData& EnsureUniqueElementData();
......
...@@ -473,7 +473,7 @@ void HTMLCanvasElement::DidDraw(const FloatRect& rect) { ...@@ -473,7 +473,7 @@ void HTMLCanvasElement::DidDraw(const FloatRect& rect) {
return; return;
if (GetLayoutObject() && GetLayoutObject()->PreviousVisibilityVisible() && if (GetLayoutObject() && GetLayoutObject()->PreviousVisibilityVisible() &&
GetDocument().GetPage()) GetDocument().GetPage())
GetDocument().GetPage()->Animator().SetHasCanvasInvalidation(true); GetDocument().GetPage()->Animator().SetHasCanvasInvalidation();
canvas_is_clear_ = false; canvas_is_clear_ = false;
if (GetLayoutObject() && !LowLatencyEnabled()) if (GetLayoutObject() && !LowLatencyEnabled())
GetLayoutObject()->SetShouldCheckForPaintInvalidation(); GetLayoutObject()->SetShouldCheckForPaintInvalidation();
......
...@@ -5,11 +5,17 @@ ...@@ -5,11 +5,17 @@
#include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/html/html_element.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
namespace blink { namespace blink {
class HTMLElementTest : public PageTestBase {}; class HTMLElementTest : public RenderingTest {
public:
HTMLElementTest()
: RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
};
TEST_F(HTMLElementTest, AdjustDirectionalityInFlatTree) { TEST_F(HTMLElementTest, AdjustDirectionalityInFlatTree) {
SetBodyContent("<bdi><summary><i id=target></i></summary></bdi>"); SetBodyContent("<bdi><summary><i id=target></i></summary></bdi>");
...@@ -18,4 +24,226 @@ TEST_F(HTMLElementTest, AdjustDirectionalityInFlatTree) { ...@@ -18,4 +24,226 @@ TEST_F(HTMLElementTest, AdjustDirectionalityInFlatTree) {
// Pass if not crashed. // Pass if not crashed.
} }
TEST_F(HTMLElementTest, DirectStyleMutationTriggered) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<div id='box'></div>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.style.width = '100px';
box.style.height = '100px';
)JS");
GetDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationNotTriggeredOnFirstFrameInDOM) {
GetDocument().GetSettings()->SetScriptEnabled(true);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.createElement('box');
document.body.appendChild(box);
box.style.width = '100px';
box.style.height = '100px';
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationNotTriggeredOnFirstPaint) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<div id='box' style='display:none'></div>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.style.display = 'block';
box.style.width = '100px';
box.style.height = '100px';
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationFromString) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<div id='box'></div>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.style = 'width:100px';
)JS");
GetDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationCustomPropertyFromString) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<div id='box'></div>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.style = '--foo:100px';
)JS");
GetDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationByAttributeStyleMap) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<div id='box'></div>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.attributeStyleMap.set('width', '100px');
)JS");
GetDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationCustomPropertyByAttributeStyleMap) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<div id='box'></div>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.attributeStyleMap.set('--foo', '100px');
)JS");
GetDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
RunDocumentLifecycle();
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationInFrame) {
SetBodyInnerHTML(R"HTML(
<iframe id='iframe'></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<div id='box'></div>
)HTML");
GetDocument().GetSettings()->SetScriptEnabled(true);
ChildDocument().GetSettings()->SetScriptEnabled(true);
EXPECT_FALSE(ChildDocument()
.GetPage()
->Animator()
.has_inline_style_mutation_for_test());
RunDocumentLifecycle();
auto* script = ChildDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.style.width = '100px';
box.style.height = '100px';
)JS");
ChildDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationNotTriggeredByInsertStyleSheet) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<div id='box'></div>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var sheet = document.createElement('style');
sheet.innerHTML = 'div { width:100px; }';
document.body.appendChild(sheet);
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest, DirectStyleMutationNotTriggeredByToggleStyleChange) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML(R"HTML(
<style>
.mystyle {
width: 100px;
}
</style>
<div id='box'></div>
)HTML");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.classList.toggle('mystyle');
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
TEST_F(HTMLElementTest,
DirectStyleMutationNotTriggeredByPseudoClassStyleChange) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML(R"HTML(
.button {
width: 50px;
height: 50px;
}
.button:focus {
width: 100px;
}
<button id='box' class='button'></button>
)HTML");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var box = document.getElementById('box');
box.focus();
)JS");
GetDocument().body()->appendChild(script);
EXPECT_EQ(GetDocument().FocusedElement(),
GetDocument().getElementById("box"));
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_inline_style_mutation_for_test());
}
} // namespace blink } // namespace blink
...@@ -121,14 +121,17 @@ void PageAnimator::PostAnimate() { ...@@ -121,14 +121,17 @@ void PageAnimator::PostAnimate() {
Clock().SetAllowedToDynamicallyUpdateTime(true); Clock().SetAllowedToDynamicallyUpdateTime(true);
} }
void PageAnimator::SetHasCanvasInvalidation(bool has_canvas_invalidation) { void PageAnimator::SetHasCanvasInvalidation() {
has_canvas_invalidation_ = has_canvas_invalidation; has_canvas_invalidation_ = true;
} }
void PageAnimator::ReportFrameAnimations(cc::AnimationHost* animation_host) { void PageAnimator::ReportFrameAnimations(cc::AnimationHost* animation_host) {
if (animation_host) if (animation_host) {
animation_host->SetHasCanvasInvalidation(has_canvas_invalidation_); animation_host->SetHasCanvasInvalidation(has_canvas_invalidation_);
animation_host->SetHasInlineStyleMutation(has_inline_style_mutation_);
}
has_canvas_invalidation_ = false; has_canvas_invalidation_ = false;
has_inline_style_mutation_ = false;
} }
void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only( void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only(
...@@ -140,6 +143,10 @@ void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only( ...@@ -140,6 +143,10 @@ void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only(
suppress_frame_requests_workaround_for704763_only_ = suppress_frame_requests; suppress_frame_requests_workaround_for704763_only_ = suppress_frame_requests;
} }
void PageAnimator::SetHasInlineStyleMutation() {
has_inline_style_mutation_ = true;
}
DISABLE_CFI_PERF DISABLE_CFI_PERF
void PageAnimator::ScheduleVisualUpdate(LocalFrame* frame) { void PageAnimator::ScheduleVisualUpdate(LocalFrame* frame) {
if (servicing_animations_ || updating_layout_and_style_for_painting_ || if (servicing_animations_ || updating_layout_and_style_for_painting_ ||
......
...@@ -49,10 +49,14 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> { ...@@ -49,10 +49,14 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
DocumentUpdateReason reason); DocumentUpdateReason reason);
AnimationClock& Clock() { return animation_clock_; } AnimationClock& Clock() { return animation_clock_; }
HeapVector<Member<Animation>> GetAnimations(const TreeScope&); HeapVector<Member<Animation>> GetAnimations(const TreeScope&);
void SetHasCanvasInvalidation(bool has_canvas_invalidation); void SetHasCanvasInvalidation();
bool has_canvas_invalidation_for_test() const { bool has_canvas_invalidation_for_test() const {
return has_canvas_invalidation_; return has_canvas_invalidation_;
} }
void SetHasInlineStyleMutation();
bool has_inline_style_mutation_for_test() const {
return has_inline_style_mutation_;
}
void ReportFrameAnimations(cc::AnimationHost* animation_host); void ReportFrameAnimations(cc::AnimationHost* animation_host);
private: private:
...@@ -61,6 +65,9 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> { ...@@ -61,6 +65,9 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
bool updating_layout_and_style_for_painting_; bool updating_layout_and_style_for_painting_;
bool suppress_frame_requests_workaround_for704763_only_ = false; bool suppress_frame_requests_workaround_for704763_only_ = false;
AnimationClock animation_clock_; AnimationClock animation_clock_;
// True if there is inline style mutation in the current frame.
bool has_inline_style_mutation_ = false;
// True if the current main frame has canvas invalidation. // True if the current main frame has canvas invalidation.
bool has_canvas_invalidation_ = false; bool has_canvas_invalidation_ = false;
}; };
......
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