Commit 449b31c0 authored by xlai's avatar xlai Committed by Commit bot

Enforce OffscreenCanvas.convertToBlob to terminate after idling for too long

BUG=657102

Review-Url: https://codereview.chromium.org/2613733002
Cr-Commit-Position: refs/heads/master@{#442059}
parent 18a3a870
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script id="myWorker" type="text/worker">
self.onmessage = function(e) {
var offscreenCanvas = new OffscreenCanvas(4, 4);
var ctx = offscreenCanvas.getContext("2d");
ctx.fillStyle = "#FF0102";
ctx.fillRect(0, 0, 4, 4);
offscreenCanvas.convertToBlob().then(function(blob) {
self.postMessage(blob);
});
};
</script>
<script type = 'text/javascript'>
// In blink Layout Tests, flag "--enable-threaded-compositing", which is
// essential for idle task running, is not turned on by default. This makes
// it convenient to test the case when CanvasAsyncBlobCreator is idling for
// too long and switching to forcing encoding in a normal task.
// Passing this test means that the enforcing mechanism in
// CanvasAsyncBlobCreator for OffscreenCanvas case is working as expected.
var w = 4;
var h = 4;
var newImg = new Image();
function imageLoaded() {
var canvas2 = document.createElement("canvas");
canvas2.width = w;
canvas2.height = h;
var ctx2 = canvas2.getContext("2d");
ctx2.drawImage(newImg, 0, 0, w, h);
var imageData = ctx2.getImageData(0, 0, w, h).data;
assert_equals(imageData[0], 255);
assert_equals(imageData[1], 1);
assert_equals(imageData[2], 2);
assert_equals(imageData[3], 255);
testImageFromOffscreen.done();
}
var workerBlob = new Blob([document.getElementById('myWorker').textContent]);
var worker = new Worker(URL.createObjectURL(workerBlob));
worker.addEventListener("message", function(msg) {
var blob = msg.data;
newImg.src = URL.createObjectURL(blob);
});
var testImageFromOffscreen = async_test(
"Check if the image loaded from blob returned by " +
"OffscreenCanvas.convertToBlob() have expected image data values.");
testImageFromOffscreen.step(function() {
newImg.onload = testImageFromOffscreen.step_func(imageLoaded);
worker.postMessage("");
});
</script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script type = 'text/javascript'>
// In blink Layout Tests, flag "--enable-threaded-compositing", which is
// essential for idle task running, is not turned on by default. This makes
// it convenient to test the case when CanvasAsyncBlobCreator is idling for
// too long and switching to forcing encoding in a normal task.
// Passing this test means that the enforcing mechanism in
// CanvasAsyncBlobCreator for OffscreenCanvas case is working as expected.
var w = 4;
var h = 4;
var newImg = new Image();
function imageLoaded() {
var canvas2 = document.createElement("canvas");
canvas2.width = w;
canvas2.height = h;
var ctx2 = canvas2.getContext("2d");
ctx2.drawImage(newImg, 0, 0, w, h);
var imageData = ctx2.getImageData(0, 0, w, h).data;
assert_equals(imageData[0], 255);
assert_equals(imageData[1], 1);
assert_equals(imageData[2], 2);
assert_equals(imageData[3], 255);
testImageFromOffscreen.done();
}
var testImageFromOffscreen = async_test(
"Check if the image loaded from blob returned by " +
"OffscreenCanvas.convertToBlob() have expected image data values.");
testImageFromOffscreen.step(function() {
newImg.onload = testImageFromOffscreen.step_func(imageLoaded);
var offscreenCanvas = new OffscreenCanvas(w, h);
var ctx = offscreenCanvas.getContext("2d");
ctx.fillStyle = "#FF0102";
ctx.fillRect(0, 0, w, h);
offscreenCanvas.convertToBlob().then(function(blob) {
newImg.src = URL.createObjectURL(blob);
});
});
</script>
......@@ -275,17 +275,13 @@ void CanvasAsyncBlobCreator::scheduleAsyncBlobCreation(const double& quality) {
NOTREACHED();
}
// TODO: Enforce OffscreenCanvas.convertToBlob to finish within deadline.
// See crbug.com/657102.
if (m_functionType == HTMLCanvasToBlobCallback) {
// We post the below task to check if the above idle task isn't late.
// There's no risk of concurrency as both tasks are on main thread.
this->postDelayedTaskToMainThread(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent,
wrapPersistent(this), quality),
IdleTaskStartTimeoutDelay);
}
// We post the below task to check if the above idle task isn't late.
// There's no risk of concurrency as both tasks are on the same thread.
this->postDelayedTaskToCurrentThread(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent,
wrapPersistent(this), quality),
IdleTaskStartTimeoutDelay);
}
}
......@@ -412,7 +408,7 @@ void CanvasAsyncBlobCreator::idleEncodeRowsJpeg(double deadlineSeconds) {
}
}
void CanvasAsyncBlobCreator::encodeRowsPngOnMainThread() {
void CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread() {
DCHECK(m_idleTaskStatus == IdleTaskSwitchedToImmediateTask);
// Continue encoding from the last completed row
......@@ -423,20 +419,39 @@ void CanvasAsyncBlobCreator::encodeRowsPngOnMainThread() {
inputPixels += m_pixelRowStride;
}
PNGImageEncoder::finalizePng(m_pngEncoderState.get());
this->createBlobAndReturnResult();
if (isMainThread()) {
this->createBlobAndReturnResult();
} else {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
crossThreadBind(&CanvasAsyncBlobCreator::createBlobAndReturnResult,
wrapCrossThreadPersistent(this)));
}
this->signalAlternativeCodePathFinishedForTesting();
}
void CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread() {
void CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread() {
DCHECK(m_idleTaskStatus == IdleTaskSwitchedToImmediateTask);
// Continue encoding from the last completed row
void (CanvasAsyncBlobCreator::*functionToBeCalled)(void);
if (JPEGImageEncoder::encodeWithPreInitializedState(
std::move(m_jpegEncoderState), m_data->data(), m_numRowsCompleted)) {
this->createBlobAndReturnResult();
functionToBeCalled = &CanvasAsyncBlobCreator::createBlobAndReturnResult;
} else {
this->createNullAndReturnResult();
functionToBeCalled = &CanvasAsyncBlobCreator::createNullAndReturnResult;
}
if (isMainThread()) {
(this->*functionToBeCalled)();
} else {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(BLINK_FROM_HERE,
crossThreadBind(functionToBeCalled,
wrapCrossThreadPersistent(this)));
}
this->signalAlternativeCodePathFinishedForTesting();
......@@ -523,7 +538,7 @@ bool CanvasAsyncBlobCreator::initializeJpegStruct(double quality) {
void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) {
if (m_idleTaskStatus == IdleTaskStarted) {
// Even if the task started quickly, we still want to ensure completion
this->postDelayedTaskToMainThread(
this->postDelayedTaskToCurrentThread(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent,
wrapPersistent(this)),
......@@ -540,8 +555,9 @@ void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread,
wrapPersistent(this)));
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread,
wrapPersistent(this)));
} else {
// Failing in initialization of png struct
this->signalAlternativeCodePathFinishedForTesting();
......@@ -552,8 +568,9 @@ void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread,
wrapPersistent(this)));
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread,
wrapPersistent(this)));
} else {
// Failing in initialization of jpeg struct
this->signalAlternativeCodePathFinishedForTesting();
......@@ -578,15 +595,17 @@ void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent() {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread,
wrapPersistent(this)));
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread,
wrapPersistent(this)));
} else {
DCHECK(m_mimeType == MimeTypeJpeg);
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread,
wrapPersistent(this)));
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread,
wrapPersistent(this)));
}
} else {
DCHECK(m_idleTaskStatus == IdleTaskFailed ||
......@@ -595,11 +614,10 @@ void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent() {
}
}
void CanvasAsyncBlobCreator::postDelayedTaskToMainThread(
void CanvasAsyncBlobCreator::postDelayedTaskToCurrentThread(
const WebTraceLocation& location,
std::unique_ptr<WTF::Closure> task,
double delayMs) {
DCHECK(isMainThread());
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postDelayedTask(location, std::move(task), delayMs);
}
......
......@@ -83,9 +83,9 @@ class CORE_EXPORT CanvasAsyncBlobCreator
virtual void scheduleInitiateJpegEncoding(const double&);
virtual void idleEncodeRowsPng(double deadlineSeconds);
virtual void idleEncodeRowsJpeg(double deadlineSeconds);
virtual void postDelayedTaskToMainThread(const WebTraceLocation&,
std::unique_ptr<WTF::Closure>,
double delayMs);
virtual void postDelayedTaskToCurrentThread(const WebTraceLocation&,
std::unique_ptr<WTF::Closure>,
double delayMs);
virtual void signalAlternativeCodePathFinishedForTesting() {}
virtual void createBlobAndReturnResult();
virtual void createNullAndReturnResult();
......@@ -126,13 +126,14 @@ class CORE_EXPORT CanvasAsyncBlobCreator
// PNG
bool initializePngStruct();
void
encodeRowsPngOnMainThread(); // Similar to idleEncodeRowsPng without deadline
void forceEncodeRowsPngOnCurrentThread(); // Similar to idleEncodeRowsPng
// without deadline
// JPEG
bool initializeJpegStruct(double quality);
void encodeRowsJpegOnMainThread(); // Similar to idleEncodeRowsJpeg without
// deadline
void forceEncodeRowsJpegOnCurrentThread(); // Similar to idleEncodeRowsJpeg
// without
// deadline
// WEBP
void encodeImageOnEncoderThread(double quality);
......
......@@ -41,16 +41,16 @@ class MockCanvasAsyncBlobCreator : public CanvasAsyncBlobCreator {
void createBlobAndReturnResult() override{};
void createNullAndReturnResult() override{};
void signalAlternativeCodePathFinishedForTesting() override;
void postDelayedTaskToMainThread(const WebTraceLocation&,
std::unique_ptr<WTF::Closure>,
double delayMs) override;
void postDelayedTaskToCurrentThread(const WebTraceLocation&,
std::unique_ptr<WTF::Closure>,
double delayMs) override;
};
void MockCanvasAsyncBlobCreator::signalAlternativeCodePathFinishedForTesting() {
testing::exitRunLoop();
}
void MockCanvasAsyncBlobCreator::postDelayedTaskToMainThread(
void MockCanvasAsyncBlobCreator::postDelayedTaskToCurrentThread(
const WebTraceLocation& location,
std::unique_ptr<WTF::Closure> task,
double delayMs) {
......
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