Commit 420ab929 authored by Xida Chen's avatar Xida Chen Committed by Commit Bot

Ship off-thread Paint Worklet

All the blockers for this feature has been removed, and it is ready to
be shipped.

Note that normally the "enable-threaded-compositing" is not enabled
when running the layout test, and off-thread paint worklet layout tests
are meaningless without the compositor thread. So this CL makes the
layout tests running on the main thread, which means we need to keep
the virtual/threaded/external/wpt/css/css-paint-api/ tests.

Bug: 829967
Change-Id: Ie7bd45e09c01729c238c1e3b8fb38e717e84e70e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1869332
Commit-Queue: Xida Chen <xidachen@chromium.org>
Reviewed-by: default avatarStephen McGruer <smcgruer@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709230}
parent 2fcf4ed8
......@@ -63,6 +63,7 @@
#include "third_party/blink/renderer/core/script/modulator.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
#include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
......@@ -335,7 +336,9 @@ static void PromiseRejectHandlerInWorker(v8::PromiseRejectMessage data) {
return;
auto* script_controller =
To<WorkerGlobalScope>(execution_context)->ScriptController();
execution_context->IsWorkerGlobalScope()
? To<WorkerGlobalScope>(execution_context)->ScriptController()
: To<WorkletGlobalScope>(execution_context)->ScriptController();
DCHECK(script_controller);
PromiseRejectHandler(data, *script_controller->GetRejectedPromises(),
......
......@@ -21,14 +21,19 @@
namespace blink {
CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name)
CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name,
bool threaded_compositing_enabled)
: CSSImageGeneratorValue(kPaintClass),
name_(name),
paint_image_generator_observer_(MakeGarbageCollected<Observer>(this)),
off_thread_paint_state_(
RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()
? OffThreadPaintState::kUnknown
: OffThreadPaintState::kMainThread) {}
(!threaded_compositing_enabled ||
!RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled())
? OffThreadPaintState::kMainThread
: OffThreadPaintState::kUnknown) {}
CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name)
: CSSPaintValue(name, Thread::CompositorThread()) {}
CSSPaintValue::CSSPaintValue(
CSSCustomIdentValue* name,
......
......@@ -20,6 +20,7 @@ namespace blink {
class CORE_EXPORT CSSPaintValue : public CSSImageGeneratorValue {
public:
explicit CSSPaintValue(CSSCustomIdentValue* name);
CSSPaintValue(CSSCustomIdentValue* name, bool threaded_compositing_enabled);
CSSPaintValue(CSSCustomIdentValue* name,
Vector<scoped_refptr<CSSVariableData>>&);
~CSSPaintValue();
......
......@@ -89,7 +89,7 @@ TEST_P(CSSPaintValueTest, ReportingCompositedUMA) {
const ComputedStyle& style = *target->Style();
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
// Mark the generator as ready - GetImage should succeed when
// OffMainThreadCSSPaint is enabled.
ON_CALL(*mock_generator, IsImageGeneratorReady()).WillByDefault(Return(true));
......@@ -135,7 +135,7 @@ TEST_P(CSSPaintValueTest, ReportingNonCompositedUMA) {
LayoutObject* target = GetLayoutObjectByElementId("target");
auto style = ComputedStyle::Create();
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
StyleGeneratedImage* style_image =
MakeGarbageCollected<StyleGeneratedImage>(*paint_value);
style->SetBorderImageSource(style_image);
......@@ -187,7 +187,7 @@ TEST_P(CSSPaintValueTest, DelayPaintUntilGeneratorReady) {
const ComputedStyle& style = *target->Style();
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
// Initially the generator is not ready, so GetImage should fail (and no paint
// should happen).
......@@ -220,7 +220,7 @@ TEST_P(CSSPaintValueTest, GetImageCalledOnMultipleDocuments) {
const ComputedStyle& style = *target->Style();
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
EXPECT_EQ(paint_value->NumberOfGeneratorsForTesting(), 0u);
paint_value->GetImage(*target, GetDocument(), style, target_size);
......@@ -238,7 +238,7 @@ TEST_P(CSSPaintValueTest, NativeInvalidationPropertiesWithNoGenerator) {
SetBodyInnerHTML(R"HTML(<div id="target"></div>)HTML");
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
EXPECT_EQ(paint_value->NumberOfGeneratorsForTesting(), 0u);
// There is no generator, so returning a nullptr.
......@@ -249,7 +249,7 @@ TEST_P(CSSPaintValueTest, CustomInvalidationPropertiesWithNoGenerator) {
SetBodyInnerHTML(R"HTML(<div id="target"></div>)HTML");
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
EXPECT_EQ(paint_value->NumberOfGeneratorsForTesting(), 0u);
// There is no generator, so returning a nullptr.
......@@ -278,7 +278,7 @@ TEST_P(CSSPaintValueTest, PrintingMustFallbackToMainThread) {
const ComputedStyle& style = *target->Style();
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
ON_CALL(*mock_generator, IsImageGeneratorReady()).WillByDefault(Return(true));
// This PW can be composited, so we should only fall back to main once, in
......@@ -319,7 +319,7 @@ TEST_P(CSSPaintValueTest, DoNotPaintForLink) {
ASSERT_NE(style.InsideLink(), EInsideLink::kNotInsideLink);
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("linkpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
EXPECT_FALSE(paint_value->GetImage(*target, GetDocument(), style,
FloatSize(100, 100)));
}
......@@ -347,14 +347,14 @@ TEST_P(CSSPaintValueTest, DoNotPaintWhenAncestorHasLink) {
ASSERT_NE(style.InsideLink(), EInsideLink::kNotInsideLink);
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("linkpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
EXPECT_FALSE(paint_value->GetImage(*target, GetDocument(), style,
FloatSize(100, 100)));
}
TEST_P(CSSPaintValueTest, BuildInputArgumentValuesNotCrash) {
auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
ASSERT_EQ(paint_value->GetParsedInputArgumentsForTesting(), nullptr);
Vector<std::unique_ptr<CrossThreadStyleValue>> cross_thread_input_arguments;
......
......@@ -49,7 +49,10 @@ PaintWorklet::PaintWorklet(LocalFrame* frame)
Supplement<LocalDOMWindow>(*frame->DomWindow()),
pending_generator_registry_(
MakeGarbageCollected<PaintWorkletPendingGeneratorRegistry>()),
worklet_id_(NextId()) {}
worklet_id_(NextId()),
is_paint_off_thread_(
RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled() &&
Thread::CompositorThread()) {}
PaintWorklet::~PaintWorklet() = default;
......@@ -58,6 +61,10 @@ void PaintWorklet::AddPendingGenerator(const String& name,
pending_generator_registry_->AddPendingGenerator(name, generator);
}
void PaintWorklet::ResetIsPaintOffThreadForTesting() {
is_paint_off_thread_ = RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled();
}
// We start with a random global scope when a new frame starts. Then within this
// frame, we switch to the other global scope after certain amount of paint
// calls (rand(kMaxPaintCountToSwitch)).
......@@ -169,10 +176,9 @@ void PaintWorklet::RegisterCSSPaintDefinition(const String& name,
// regiserered from RegisterCSSPaintDefinition and one extra definition from
// RegisterMainThreadDocumentPaintDefinition if OffMainThreadCSSPaintEnabled
// is true.
unsigned required_registered_count =
RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()
? kNumGlobalScopesPerThread + 1
: kNumGlobalScopesPerThread;
unsigned required_registered_count = is_paint_off_thread_
? kNumGlobalScopesPerThread + 1
: kNumGlobalScopesPerThread;
if (existing_document_definition->GetRegisteredDefinitionCount() ==
required_registered_count)
pending_generator_registry_->NotifyGeneratorReady(name);
......@@ -229,7 +235,7 @@ void PaintWorklet::RegisterMainThreadDocumentPaintDefinition(
bool PaintWorklet::NeedsToCreateGlobalScope() {
wtf_size_t num_scopes_needed = kNumGlobalScopesPerThread;
// If we are running off main thread, we will need twice as many global scopes
if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled())
if (is_paint_off_thread_)
num_scopes_needed *= 2;
return GetNumberOfGlobalScopes() < num_scopes_needed;
}
......@@ -241,7 +247,7 @@ WorkletGlobalScopeProxy* PaintWorklet::CreateGlobalScope() {
// scopes from the beginning of the vector. If this code is changed to put
// the main thread global scopes at the end, then SelectNewGlobalScope must
// also be changed.
if (!RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled() ||
if (!is_paint_off_thread_ ||
GetNumberOfGlobalScopes() < kNumGlobalScopesPerThread) {
return MakeGarbageCollected<PaintWorkletGlobalScopeProxy>(
To<Document>(GetExecutionContext())->GetFrame(), ModuleResponsesMap(),
......
......@@ -89,6 +89,8 @@ class MODULES_EXPORT PaintWorklet : public Worklet,
proxy_client_ = proxy_client;
}
void ResetIsPaintOffThreadForTesting();
protected:
// Since paint worklet has more than one global scope, we MUST override this
// function and provide our own selection logic.
......@@ -140,6 +142,12 @@ class MODULES_EXPORT PaintWorklet : public Worklet,
// to ensure that all global scopes get the same proxy client.
Member<PaintWorkletProxyClient> proxy_client_;
// When running layout test, paint worklet has to be on the main thread
// because "enable-threaded-compositing" is off by default. However, some unit
// tests may be testing the functionality of the APIs when the paint worklet
// is off the main thread.
bool is_paint_off_thread_;
DISALLOW_COPY_AND_ASSIGN(PaintWorklet);
};
......
......@@ -26,7 +26,9 @@
namespace blink {
class TestPaintWorklet : public PaintWorklet {
public:
explicit TestPaintWorklet(LocalFrame* frame) : PaintWorklet(frame) {}
explicit TestPaintWorklet(LocalFrame* frame) : PaintWorklet(frame) {
ResetIsPaintOffThreadForTesting();
}
void SetPaintsToSwitch(int num) { paints_to_switch_ = num; }
......@@ -158,8 +160,14 @@ TEST_F(PaintWorkletTest, SinglyRegisteredDocumentDefinitionNotUsed) {
EXPECT_TRUE(generator);
EXPECT_EQ(generator->GetRegisteredDefinitionCountForTesting(), 1u);
DocumentPaintDefinition* definition;
EXPECT_FALSE(generator->GetValidDocumentDefinitionForTesting(definition));
EXPECT_FALSE(definition);
// Please refer to CSSPaintImageGeneratorImpl::GetValidDocumentDefinition for
// the logic.
if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
EXPECT_TRUE(generator->GetValidDocumentDefinitionForTesting(definition));
} else {
EXPECT_FALSE(generator->GetValidDocumentDefinitionForTesting(definition));
EXPECT_FALSE(definition);
}
}
// In this test, we set a list of "paints_to_switch" numbers, and in each frame,
......@@ -223,6 +231,7 @@ class MockObserver final : public CSSPaintImageGenerator::Observer {
TEST_P(MainOrOffThreadPaintWorkletTest, ConsistentGlobalScopeOnMainThread) {
PaintWorklet* paint_worklet_to_test =
PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
MockObserver* observer = MakeGarbageCollected<MockObserver>();
CSSPaintImageGeneratorImpl* generator_foo =
......@@ -294,6 +303,7 @@ TEST_P(MainOrOffThreadPaintWorkletTest, ConsistentGlobalScopeOnMainThread) {
TEST_P(MainOrOffThreadPaintWorkletTest, AllGlobalScopesMustBeCreated) {
PaintWorklet* paint_worklet_to_test =
MakeGarbageCollected<PaintWorklet>(&GetFrame());
paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
EXPECT_TRUE(paint_worklet_to_test->GetGlobalScopesForTesting().IsEmpty());
......@@ -321,6 +331,7 @@ TEST_F(PaintWorkletTest, ConsistentGlobalScopeCrossThread) {
ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true);
PaintWorklet* paint_worklet_to_test =
PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
MockObserver* observer = MakeGarbageCollected<MockObserver>();
CSSPaintImageGeneratorImpl* generator_foo =
......@@ -490,6 +501,7 @@ TEST_F(PaintWorkletTest, GeneratorNotifiedAfterAllRegistrations) {
ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true);
PaintWorklet* paint_worklet_to_test =
PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
MockObserver* observer = MakeGarbageCollected<MockObserver>();
CSSPaintImageGeneratorImpl* generator =
......
......@@ -1100,6 +1100,7 @@
},
{
name: "OffMainThreadCSSPaint",
status: "stable",
},
{
name: "OffscreenCanvasCommit",
......
CONSOLE MESSAGE: line 9: This test logs a result once from each PaintWorkletGlobalScope
CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
This is a testharness.js-based test.
PASS Dynamic import() on WorkletGlobalScope should reject the promise.
Harness: the test ran to completion.
CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
This is a testharness.js-based test.
PASS eval() call on the worklet should be blocked because the script-src unsafe-eval directive is not specified.
PASS eval() call on the worklet should not be blocked because the script-src unsafe-eval directive allows it.
Harness: the test ran to completion.
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