blob: 99d80457fbc82d9b94c6922e0466eafe9ec50180 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/html/canvas/CanvasAsyncBlobCreator.h"
#include "core/dom/DOMException.h"
#include "core/dom/Document.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/fileapi/Blob.h"
#include "platform/CrossThreadFunctional.h"
#include "platform/Histogram.h"
#include "platform/WebTaskRunner.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/image-encoders/JPEGImageEncoder.h"
#include "platform/image-encoders/PNGImageEncoder.h"
#include "platform/threading/BackgroundTaskRunner.h"
#include "public/platform/Platform.h"
#include "public/platform/WebScheduler.h"
#include "public/platform/WebThread.h"
#include "public/platform/WebTraceLocation.h"
#include "wtf/CurrentTime.h"
#include "wtf/Functional.h"
#include "wtf/PtrUtil.h"
namespace blink {
namespace {
const double SlackBeforeDeadline =
0.001; // a small slack period between deadline and current time for safety
const int NumChannelsPng = 4;
// The encoding task is highly likely to switch from idle task to alternative
// code path when the startTimeoutDelay is set to be below 150ms. As we want the
// majority of encoding tasks to take the usual async idle task, we set a
// lenient limit -- 200ms here. This limit still needs to be short enough for
// the latency to be negligible to the user.
const double IdleTaskStartTimeoutDelay = 200.0;
// We should be more lenient on completion timeout delay to ensure that the
// switch from idle to main thread only happens to a minority of toBlob calls
#if !OS(ANDROID)
// Png image encoding on 4k by 4k canvas on Mac HDD takes 5.7+ seconds
const double IdleTaskCompleteTimeoutDelay = 6700.0;
#else
// Png image encoding on 4k by 4k canvas on Android One takes 9.0+ seconds
const double IdleTaskCompleteTimeoutDelay = 10000.0;
#endif
bool isDeadlineNearOrPassed(double deadlineSeconds) {
return (deadlineSeconds - SlackBeforeDeadline -
monotonicallyIncreasingTime() <=
0);
}
String convertMimeTypeEnumToString(
CanvasAsyncBlobCreator::MimeType mimeTypeEnum) {
switch (mimeTypeEnum) {
case CanvasAsyncBlobCreator::MimeTypePng:
return "image/png";
case CanvasAsyncBlobCreator::MimeTypeJpeg:
return "image/jpeg";
case CanvasAsyncBlobCreator::MimeTypeWebp:
return "image/webp";
default:
return "image/unknown";
}
}
CanvasAsyncBlobCreator::MimeType convertMimeTypeStringToEnum(
const String& mimeType) {
CanvasAsyncBlobCreator::MimeType mimeTypeEnum;
if (mimeType == "image/png") {
mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypePng;
} else if (mimeType == "image/jpeg") {
mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeJpeg;
} else if (mimeType == "image/webp") {
mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeWebp;
} else {
mimeTypeEnum = CanvasAsyncBlobCreator::NumberOfMimeTypeSupported;
}
return mimeTypeEnum;
}
void recordIdleTaskStatusHistogram(
CanvasAsyncBlobCreator::IdleTaskStatus status) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
EnumerationHistogram, toBlobIdleTaskStatus,
new EnumerationHistogram("Blink.Canvas.ToBlob.IdleTaskStatus",
CanvasAsyncBlobCreator::IdleTaskCount));
toBlobIdleTaskStatus.count(status);
}
// This enum is used in histogram and any more types should be appended at the
// end of the list.
enum ElapsedTimeHistogramType {
InitiateEncodingDelay,
IdleEncodeDuration,
ToBlobDuration,
NumberOfElapsedTimeHistogramTypes
};
void recordElapsedTimeHistogram(
ElapsedTimeHistogramType type,
CanvasAsyncBlobCreator::MimeType mimeType,
double elapsedTime) {
if (type == InitiateEncodingDelay) {
if (mimeType == CanvasAsyncBlobCreator::MimeTypePng) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, toBlobPNGInitiateEncodingCounter,
new CustomCountHistogram(
"Blink.Canvas.ToBlob.InitiateEncodingDelay.PNG", 0, 10000000,
50));
toBlobPNGInitiateEncodingCounter.count(elapsedTime * 1000000.0);
} else if (mimeType == CanvasAsyncBlobCreator::MimeTypeJpeg) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, toBlobJPEGInitiateEncodingCounter,
new CustomCountHistogram(
"Blink.Canvas.ToBlob.InitiateEncodingDelay.JPEG", 0, 10000000,
50));
toBlobJPEGInitiateEncodingCounter.count(elapsedTime * 1000000.0);
}
} else if (type == IdleEncodeDuration) {
if (mimeType == CanvasAsyncBlobCreator::MimeTypePng) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, toBlobPNGIdleEncodeCounter,
new CustomCountHistogram("Blink.Canvas.ToBlob.IdleEncodeDuration.PNG",
0, 10000000, 50));
toBlobPNGIdleEncodeCounter.count(elapsedTime * 1000000.0);
} else if (mimeType == CanvasAsyncBlobCreator::MimeTypeJpeg) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, toBlobJPEGIdleEncodeCounter,
new CustomCountHistogram(
"Blink.Canvas.ToBlob.IdleEncodeDuration.JPEG", 0, 10000000, 50));
toBlobJPEGIdleEncodeCounter.count(elapsedTime * 1000000.0);
}
} else if (type == ToBlobDuration) {
if (mimeType == CanvasAsyncBlobCreator::MimeTypePng) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, toBlobPNGCounter,
new CustomCountHistogram("Blink.Canvas.ToBlobDuration.PNG", 0,
10000000, 50));
toBlobPNGCounter.count(elapsedTime * 1000000.0);
} else if (mimeType == CanvasAsyncBlobCreator::MimeTypeJpeg) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, toBlobJPEGCounter,
new CustomCountHistogram("Blink.Canvas.ToBlobDuration.JPEG", 0,
10000000, 50));
toBlobJPEGCounter.count(elapsedTime * 1000000.0);
} else if (mimeType == CanvasAsyncBlobCreator::MimeTypeWebp) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, toBlobWEBPCounter,
new CustomCountHistogram("Blink.Canvas.ToBlobDuration.WEBP", 0,
10000000, 50));
toBlobWEBPCounter.count(elapsedTime * 1000000.0);
}
}
}
} // anonymous namespace
CanvasAsyncBlobCreator* CanvasAsyncBlobCreator::create(
DOMUint8ClampedArray* unpremultipliedRGBAImageData,
const String& mimeType,
const IntSize& size,
BlobCallback* callback,
double startTime,
Document* document) {
return new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData,
convertMimeTypeStringToEnum(mimeType), size,
callback, startTime, document, nullptr);
}
CanvasAsyncBlobCreator* CanvasAsyncBlobCreator::create(
DOMUint8ClampedArray* unpremultipliedRGBAImageData,
const String& mimeType,
const IntSize& size,
double startTime,
Document* document,
ScriptPromiseResolver* resolver) {
return new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData,
convertMimeTypeStringToEnum(mimeType), size,
nullptr, startTime, document, resolver);
}
CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data,
MimeType mimeType,
const IntSize& size,
BlobCallback* callback,
double startTime,
Document* document,
ScriptPromiseResolver* resolver)
: m_data(data),
m_document(document),
m_size(size),
m_mimeType(mimeType),
m_startTime(startTime),
m_elapsedTime(0),
m_callback(callback),
m_scriptPromiseResolver(resolver) {
DCHECK(m_data->length() == (unsigned)(size.height() * size.width() * 4));
m_encodedImage = WTF::wrapUnique(new Vector<unsigned char>());
m_pixelRowStride = size.width() * NumChannelsPng;
m_idleTaskStatus = IdleTaskNotSupported;
m_numRowsCompleted = 0;
if (document) {
m_parentFrameTaskRunner = ParentFrameTaskRunners::create(document->frame());
}
if (m_scriptPromiseResolver) {
m_functionType = OffscreenCanvasToBlobPromise;
} else {
m_functionType = HTMLCanvasToBlobCallback;
}
}
CanvasAsyncBlobCreator::~CanvasAsyncBlobCreator() {}
void CanvasAsyncBlobCreator::dispose() {
// Eagerly let go of references to prevent retention of these
// resources while any remaining posted tasks are queued.
m_data.clear();
m_document.clear();
m_parentFrameTaskRunner.clear();
m_callback.clear();
m_scriptPromiseResolver.clear();
}
void CanvasAsyncBlobCreator::scheduleAsyncBlobCreation(const double& quality) {
if (m_mimeType == MimeTypeWebp) {
if (!isMainThread()) {
DCHECK(m_functionType == OffscreenCanvasToBlobPromise);
// When OffscreenCanvas.convertToBlob() occurs on worker thread,
// we do not need to use background task runner to reduce load on main.
// So we just directly encode images on the worker thread.
if (!ImageDataBuffer(m_size, m_data->data())
.encodeImage("image/webp", quality, m_encodedImage.get())) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::createNullAndReturnResult,
wrapPersistent(this)));
return;
}
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::createBlobAndReturnResult,
wrapPersistent(this)));
} else {
BackgroundTaskRunner::postOnBackgroundThread(
BLINK_FROM_HERE,
crossThreadBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread,
wrapCrossThreadPersistent(this), quality));
}
} else {
m_idleTaskStatus = IdleTaskNotStarted;
if (m_mimeType == MimeTypePng) {
this->scheduleInitiatePngEncoding();
} else if (m_mimeType == MimeTypeJpeg) {
this->scheduleInitiateJpegEncoding(quality);
} else {
// Progressive encoding is only applicable to png and jpeg image format,
// and thus idle tasks scheduling can only be applied to these image
// formats.
// TODO(xlai): Progressive encoding on webp image formats
// (crbug.com/571399)
NOTREACHED();
}
// 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);
}
}
void CanvasAsyncBlobCreator::scheduleInitiateJpegEncoding(
const double& quality) {
m_scheduleInitiateStartTime = WTF::monotonicallyIncreasingTime();
Platform::current()->currentThread()->scheduler()->postIdleTask(
BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::initiateJpegEncoding,
wrapPersistent(this), quality));
}
void CanvasAsyncBlobCreator::initiateJpegEncoding(const double& quality,
double deadlineSeconds) {
recordElapsedTimeHistogram(
InitiateEncodingDelay, MimeTypeJpeg,
WTF::monotonicallyIncreasingTime() - m_scheduleInitiateStartTime);
if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) {
return;
}
DCHECK(m_idleTaskStatus == IdleTaskNotStarted);
m_idleTaskStatus = IdleTaskStarted;
if (!initializeJpegStruct(quality)) {
m_idleTaskStatus = IdleTaskFailed;
return;
}
this->idleEncodeRowsJpeg(deadlineSeconds);
}
void CanvasAsyncBlobCreator::scheduleInitiatePngEncoding() {
m_scheduleInitiateStartTime = WTF::monotonicallyIncreasingTime();
Platform::current()->currentThread()->scheduler()->postIdleTask(
BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::initiatePngEncoding,
wrapPersistent(this)));
}
void CanvasAsyncBlobCreator::initiatePngEncoding(double deadlineSeconds) {
recordElapsedTimeHistogram(
InitiateEncodingDelay, MimeTypePng,
WTF::monotonicallyIncreasingTime() - m_scheduleInitiateStartTime);
if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) {
return;
}
DCHECK(m_idleTaskStatus == IdleTaskNotStarted);
m_idleTaskStatus = IdleTaskStarted;
if (!initializePngStruct()) {
m_idleTaskStatus = IdleTaskFailed;
return;
}
this->idleEncodeRowsPng(deadlineSeconds);
}
void CanvasAsyncBlobCreator::idleEncodeRowsPng(double deadlineSeconds) {
if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) {
return;
}
double startTime = WTF::monotonicallyIncreasingTime();
unsigned char* inputPixels =
m_data->data() + m_pixelRowStride * m_numRowsCompleted;
for (int y = m_numRowsCompleted; y < m_size.height(); ++y) {
if (isDeadlineNearOrPassed(deadlineSeconds)) {
m_numRowsCompleted = y;
m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime);
Platform::current()->currentThread()->scheduler()->postIdleTask(
BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::idleEncodeRowsPng,
wrapPersistent(this)));
return;
}
PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get());
inputPixels += m_pixelRowStride;
}
m_numRowsCompleted = m_size.height();
PNGImageEncoder::finalizePng(m_pngEncoderState.get());
m_idleTaskStatus = IdleTaskCompleted;
m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime);
recordElapsedTimeHistogram(IdleEncodeDuration, MimeTypePng, m_elapsedTime);
if (isDeadlineNearOrPassed(deadlineSeconds)) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::createBlobAndReturnResult,
wrapPersistent(this)));
} else {
this->createBlobAndReturnResult();
}
}
void CanvasAsyncBlobCreator::idleEncodeRowsJpeg(double deadlineSeconds) {
if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) {
return;
}
double startTime = WTF::monotonicallyIncreasingTime();
m_numRowsCompleted = JPEGImageEncoder::progressiveEncodeRowsJpegHelper(
m_jpegEncoderState.get(), m_data->data(), m_numRowsCompleted,
SlackBeforeDeadline, deadlineSeconds);
m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime);
if (m_numRowsCompleted == m_size.height()) {
m_idleTaskStatus = IdleTaskCompleted;
recordElapsedTimeHistogram(IdleEncodeDuration, MimeTypeJpeg, m_elapsedTime);
if (isDeadlineNearOrPassed(deadlineSeconds)) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::createBlobAndReturnResult,
wrapPersistent(this)));
} else {
this->createBlobAndReturnResult();
}
} else if (m_numRowsCompleted == JPEGImageEncoder::ProgressiveEncodeFailed) {
m_idleTaskStatus = IdleTaskFailed;
this->createNullAndReturnResult();
} else {
Platform::current()->currentThread()->scheduler()->postIdleTask(
BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::idleEncodeRowsJpeg,
wrapPersistent(this)));
}
}
void CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread() {
DCHECK(m_idleTaskStatus == IdleTaskSwitchedToImmediateTask);
// Continue encoding from the last completed row
unsigned char* inputPixels =
m_data->data() + m_pixelRowStride * m_numRowsCompleted;
for (int y = m_numRowsCompleted; y < m_size.height(); ++y) {
PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get());
inputPixels += m_pixelRowStride;
}
PNGImageEncoder::finalizePng(m_pngEncoderState.get());
if (isMainThread()) {
this->createBlobAndReturnResult();
} else {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
crossThreadBind(&CanvasAsyncBlobCreator::createBlobAndReturnResult,
wrapCrossThreadPersistent(this)));
}
this->signalAlternativeCodePathFinishedForTesting();
}
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)) {
functionToBeCalled = &CanvasAsyncBlobCreator::createBlobAndReturnResult;
} else {
functionToBeCalled = &CanvasAsyncBlobCreator::createNullAndReturnResult;
}
if (isMainThread()) {
(this->*functionToBeCalled)();
} else {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(BLINK_FROM_HERE,
crossThreadBind(functionToBeCalled,
wrapCrossThreadPersistent(this)));
}
this->signalAlternativeCodePathFinishedForTesting();
}
void CanvasAsyncBlobCreator::createBlobAndReturnResult() {
recordIdleTaskStatusHistogram(m_idleTaskStatus);
recordElapsedTimeHistogram(ToBlobDuration, m_mimeType,
WTF::monotonicallyIncreasingTime() - m_startTime);
Blob* resultBlob =
Blob::create(m_encodedImage->data(), m_encodedImage->size(),
convertMimeTypeEnumToString(m_mimeType));
if (m_functionType == HTMLCanvasToBlobCallback) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(BLINK_FROM_HERE, WTF::bind(&BlobCallback::handleEvent,
wrapPersistent(m_callback.get()),
wrapPersistent(resultBlob)));
} else {
m_scriptPromiseResolver->resolve(resultBlob);
}
// Avoid unwanted retention, see dispose().
dispose();
}
void CanvasAsyncBlobCreator::createNullAndReturnResult() {
recordIdleTaskStatusHistogram(m_idleTaskStatus);
if (m_functionType == HTMLCanvasToBlobCallback) {
DCHECK(isMainThread());
recordIdleTaskStatusHistogram(m_idleTaskStatus);
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(BLINK_FROM_HERE,
WTF::bind(&BlobCallback::handleEvent,
wrapPersistent(m_callback.get()), nullptr));
} else {
m_scriptPromiseResolver->reject(DOMException::create(
EncodingError, "Encoding of the source image has failed."));
}
// Avoid unwanted retention, see dispose().
dispose();
}
void CanvasAsyncBlobCreator::encodeImageOnEncoderThread(double quality) {
DCHECK(!isMainThread());
DCHECK(m_mimeType == MimeTypeWebp);
if (!ImageDataBuffer(m_size, m_data->data())
.encodeImage("image/webp", quality, m_encodedImage.get())) {
m_parentFrameTaskRunner->get(TaskType::CanvasBlobSerialization)
->postTask(
BLINK_FROM_HERE,
crossThreadBind(&CanvasAsyncBlobCreator::createNullAndReturnResult,
wrapCrossThreadPersistent(this)));
return;
}
m_parentFrameTaskRunner->get(TaskType::CanvasBlobSerialization)
->postTask(
BLINK_FROM_HERE,
crossThreadBind(&CanvasAsyncBlobCreator::createBlobAndReturnResult,
wrapCrossThreadPersistent(this)));
}
bool CanvasAsyncBlobCreator::initializePngStruct() {
m_pngEncoderState =
PNGImageEncoderState::create(m_size, m_encodedImage.get());
if (!m_pngEncoderState) {
this->createNullAndReturnResult();
return false;
}
return true;
}
bool CanvasAsyncBlobCreator::initializeJpegStruct(double quality) {
m_jpegEncoderState =
JPEGImageEncoderState::create(m_size, quality, m_encodedImage.get());
if (!m_jpegEncoderState) {
this->createNullAndReturnResult();
return false;
}
return true;
}
void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) {
if (m_idleTaskStatus == IdleTaskStarted) {
// Even if the task started quickly, we still want to ensure completion
this->postDelayedTaskToCurrentThread(
BLINK_FROM_HERE,
WTF::bind(&CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent,
wrapPersistent(this)),
IdleTaskCompleteTimeoutDelay);
} else if (m_idleTaskStatus == IdleTaskNotStarted) {
// If the idle task does not start after a delay threshold, we will
// force it to happen on main thread (even though it may cause more
// janks) to prevent toBlob being postponed forever in extreme cases.
m_idleTaskStatus = IdleTaskSwitchedToImmediateTask;
signalTaskSwitchInStartTimeoutEventForTesting();
if (m_mimeType == MimeTypePng) {
if (initializePngStruct()) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread,
wrapPersistent(this)));
} else {
// Failing in initialization of png struct
this->signalAlternativeCodePathFinishedForTesting();
}
} else {
DCHECK(m_mimeType == MimeTypeJpeg);
if (initializeJpegStruct(quality)) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread,
wrapPersistent(this)));
} else {
// Failing in initialization of jpeg struct
this->signalAlternativeCodePathFinishedForTesting();
}
}
} else {
DCHECK(m_idleTaskStatus == IdleTaskFailed ||
m_idleTaskStatus == IdleTaskCompleted);
this->signalAlternativeCodePathFinishedForTesting();
}
}
void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent() {
DCHECK(m_idleTaskStatus != IdleTaskNotStarted);
if (m_idleTaskStatus == IdleTaskStarted) {
// It has taken too long to complete for the idle task.
m_idleTaskStatus = IdleTaskSwitchedToImmediateTask;
signalTaskSwitchInCompleteTimeoutEventForTesting();
if (m_mimeType == MimeTypePng) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsPngOnCurrentThread,
wrapPersistent(this)));
} else {
DCHECK(m_mimeType == MimeTypeJpeg);
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postTask(
BLINK_FROM_HERE,
WTF::bind(
&CanvasAsyncBlobCreator::forceEncodeRowsJpegOnCurrentThread,
wrapPersistent(this)));
}
} else {
DCHECK(m_idleTaskStatus == IdleTaskFailed ||
m_idleTaskStatus == IdleTaskCompleted);
this->signalAlternativeCodePathFinishedForTesting();
}
}
void CanvasAsyncBlobCreator::postDelayedTaskToCurrentThread(
const WebTraceLocation& location,
std::unique_ptr<WTF::Closure> task,
double delayMs) {
TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document)
->postDelayedTask(location, std::move(task), delayMs);
}
DEFINE_TRACE(CanvasAsyncBlobCreator) {
visitor->trace(m_document);
visitor->trace(m_data);
visitor->trace(m_callback);
visitor->trace(m_parentFrameTaskRunner);
visitor->trace(m_scriptPromiseResolver);
}
} // namespace blink