OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/html/canvas/CanvasAsyncBlobCreator.h" | 5 #include "core/html/canvas/CanvasAsyncBlobCreator.h" |
6 | 6 |
7 #include "core/dom/Document.h" | 7 #include "core/dom/Document.h" |
8 #include "core/dom/TaskRunnerHelper.h" | 8 #include "core/dom/TaskRunnerHelper.h" |
9 #include "core/fileapi/Blob.h" | 9 #include "core/fileapi/Blob.h" |
10 #include "platform/CrossThreadFunctional.h" | 10 #include "platform/CrossThreadFunctional.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeJpeg; | 79 mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeJpeg; |
80 } else if (mimeType == "image/webp") { | 80 } else if (mimeType == "image/webp") { |
81 mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeWebp; | 81 mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeWebp; |
82 } else { | 82 } else { |
83 mimeTypeEnum = CanvasAsyncBlobCreator::NumberOfMimeTypeSupported; | 83 mimeTypeEnum = CanvasAsyncBlobCreator::NumberOfMimeTypeSupported; |
84 } | 84 } |
85 return mimeTypeEnum; | 85 return mimeTypeEnum; |
86 } | 86 } |
87 | 87 |
88 void recordIdleTaskStatusHistogram( | 88 void recordIdleTaskStatusHistogram( |
| 89 CanvasAsyncBlobCreator::ToBlobFunctionType functionType, |
89 CanvasAsyncBlobCreator::IdleTaskStatus status) { | 90 CanvasAsyncBlobCreator::IdleTaskStatus status) { |
| 91 // TODO(crbug.com/653599): Add histograms for OffscreenCanvas.convertToBlob. |
| 92 if (functionType == CanvasAsyncBlobCreator::OffscreenCanvasToBlobPromise) |
| 93 return; |
90 DEFINE_STATIC_LOCAL(EnumerationHistogram, toBlobIdleTaskStatus, | 94 DEFINE_STATIC_LOCAL(EnumerationHistogram, toBlobIdleTaskStatus, |
91 ("Blink.Canvas.ToBlob.IdleTaskStatus", | 95 ("Blink.Canvas.ToBlob.IdleTaskStatus", |
92 CanvasAsyncBlobCreator::IdleTaskCount)); | 96 CanvasAsyncBlobCreator::IdleTaskCount)); |
93 toBlobIdleTaskStatus.count(status); | 97 toBlobIdleTaskStatus.count(status); |
94 } | 98 } |
95 | 99 |
96 // This enum is used in histogram and any more types should be appended at the | 100 // This enum is used in histogram and any more types should be appended at the |
97 // end of the list. | 101 // end of the list. |
98 enum ElapsedTimeHistogramType { | 102 enum ElapsedTimeHistogramType { |
99 InitiateEncodingDelay, | 103 InitiateEncodingDelay, |
100 IdleEncodeDuration, | 104 IdleEncodeDuration, |
101 ToBlobDuration, | 105 ToBlobDuration, |
102 NumberOfElapsedTimeHistogramTypes | 106 NumberOfElapsedTimeHistogramTypes |
103 }; | 107 }; |
104 | 108 |
105 void recordElapsedTimeHistogram(ElapsedTimeHistogramType type, | 109 void recordElapsedTimeHistogram( |
106 CanvasAsyncBlobCreator::MimeType mimeType, | 110 CanvasAsyncBlobCreator::ToBlobFunctionType functionType, |
107 double elapsedTime) { | 111 ElapsedTimeHistogramType type, |
| 112 CanvasAsyncBlobCreator::MimeType mimeType, |
| 113 double elapsedTime) { |
| 114 // TODO(crbug.com/653599): Add histograms for OffscreenCanvas.convertToBlob. |
| 115 if (functionType == CanvasAsyncBlobCreator::OffscreenCanvasToBlobPromise) |
| 116 return; |
| 117 |
108 if (type == InitiateEncodingDelay) { | 118 if (type == InitiateEncodingDelay) { |
109 if (mimeType == CanvasAsyncBlobCreator::MimeTypePng) { | 119 if (mimeType == CanvasAsyncBlobCreator::MimeTypePng) { |
110 DEFINE_STATIC_LOCAL( | 120 DEFINE_STATIC_LOCAL( |
111 CustomCountHistogram, toBlobPNGInitiateEncodingCounter, | 121 CustomCountHistogram, toBlobPNGInitiateEncodingCounter, |
112 ("Blink.Canvas.ToBlob.InitiateEncodingDelay.PNG", 0, 10000000, 50)); | 122 ("Blink.Canvas.ToBlob.InitiateEncodingDelay.PNG", 0, 10000000, 50)); |
113 toBlobPNGInitiateEncodingCounter.count(elapsedTime * 1000000.0); | 123 toBlobPNGInitiateEncodingCounter.count(elapsedTime * 1000000.0); |
114 } else if (mimeType == CanvasAsyncBlobCreator::MimeTypeJpeg) { | 124 } else if (mimeType == CanvasAsyncBlobCreator::MimeTypeJpeg) { |
115 DEFINE_STATIC_LOCAL( | 125 DEFINE_STATIC_LOCAL( |
116 CustomCountHistogram, toBlobJPEGInitiateEncodingCounter, | 126 CustomCountHistogram, toBlobJPEGInitiateEncodingCounter, |
117 ("Blink.Canvas.ToBlob.InitiateEncodingDelay.JPEG", 0, 10000000, 50)); | 127 ("Blink.Canvas.ToBlob.InitiateEncodingDelay.JPEG", 0, 10000000, 50)); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 } | 159 } |
150 | 160 |
151 } // anonymous namespace | 161 } // anonymous namespace |
152 | 162 |
153 CanvasAsyncBlobCreator* CanvasAsyncBlobCreator::create( | 163 CanvasAsyncBlobCreator* CanvasAsyncBlobCreator::create( |
154 DOMUint8ClampedArray* unpremultipliedRGBAImageData, | 164 DOMUint8ClampedArray* unpremultipliedRGBAImageData, |
155 const String& mimeType, | 165 const String& mimeType, |
156 const IntSize& size, | 166 const IntSize& size, |
157 BlobCallback* callback, | 167 BlobCallback* callback, |
158 double startTime, | 168 double startTime, |
159 Document& document) { | 169 Document* document) { |
160 return new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData, | 170 return new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData, |
161 convertMimeTypeStringToEnum(mimeType), size, | 171 convertMimeTypeStringToEnum(mimeType), size, |
162 callback, startTime, document); | 172 callback, startTime, document, nullptr); |
| 173 } |
| 174 |
| 175 CanvasAsyncBlobCreator* CanvasAsyncBlobCreator::create( |
| 176 DOMUint8ClampedArray* unpremultipliedRGBAImageData, |
| 177 const String& mimeType, |
| 178 const IntSize& size, |
| 179 double startTime, |
| 180 Document* document, |
| 181 ScriptPromiseResolver* resolver) { |
| 182 return new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData, |
| 183 convertMimeTypeStringToEnum(mimeType), size, |
| 184 nullptr, startTime, document, resolver); |
163 } | 185 } |
164 | 186 |
165 CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data, | 187 CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data, |
166 MimeType mimeType, | 188 MimeType mimeType, |
167 const IntSize& size, | 189 const IntSize& size, |
168 BlobCallback* callback, | 190 BlobCallback* callback, |
169 double startTime, | 191 double startTime, |
170 Document& document) | 192 Document* document, |
| 193 ScriptPromiseResolver* resolver) |
171 : m_data(data), | 194 : m_data(data), |
172 m_document(&document), | 195 m_document(document), |
173 m_size(size), | 196 m_size(size), |
174 m_mimeType(mimeType), | 197 m_mimeType(mimeType), |
175 m_callback(callback), | |
176 m_startTime(startTime), | 198 m_startTime(startTime), |
177 m_elapsedTime(0), | 199 m_elapsedTime(0), |
178 m_parentFrameTaskRunner( | 200 m_callback(callback), |
179 ParentFrameTaskRunners::create(document.frame())) { | 201 m_scriptPromiseResolver(resolver) { |
180 ASSERT(m_data->length() == (unsigned)(size.height() * size.width() * 4)); | 202 DCHECK(m_data->length() == (unsigned)(size.height() * size.width() * 4)); |
181 m_encodedImage = wrapUnique(new Vector<unsigned char>()); | 203 m_encodedImage = wrapUnique(new Vector<unsigned char>()); |
182 m_pixelRowStride = size.width() * NumChannelsPng; | 204 m_pixelRowStride = size.width() * NumChannelsPng; |
183 m_idleTaskStatus = IdleTaskNotSupported; | 205 m_idleTaskStatus = IdleTaskNotSupported; |
184 m_numRowsCompleted = 0; | 206 m_numRowsCompleted = 0; |
| 207 if (document) { |
| 208 m_parentFrameTaskRunner = ParentFrameTaskRunners::create(document->frame()); |
| 209 } |
| 210 if (m_scriptPromiseResolver) { |
| 211 m_functionType = OffscreenCanvasToBlobPromise; |
| 212 } else { |
| 213 m_functionType = HTMLCanvasToBlobCallback; |
| 214 } |
185 } | 215 } |
186 | 216 |
187 CanvasAsyncBlobCreator::~CanvasAsyncBlobCreator() {} | 217 CanvasAsyncBlobCreator::~CanvasAsyncBlobCreator() {} |
188 | 218 |
189 void CanvasAsyncBlobCreator::dispose() { | 219 void CanvasAsyncBlobCreator::dispose() { |
190 // Eagerly let go of references to prevent retention of these | 220 // Eagerly let go of references to prevent retention of these |
191 // resources while any remaining posted tasks are queued. | 221 // resources while any remaining posted tasks are queued. |
192 m_data.clear(); | 222 m_data.clear(); |
193 m_document.clear(); | 223 m_document.clear(); |
| 224 m_parentFrameTaskRunner.clear(); |
194 m_callback.clear(); | 225 m_callback.clear(); |
| 226 m_scriptPromiseResolver.clear(); |
195 } | 227 } |
196 | 228 |
197 void CanvasAsyncBlobCreator::scheduleAsyncBlobCreation( | 229 void CanvasAsyncBlobCreator::scheduleAsyncBlobCreation(const double& quality) { |
198 bool canUseIdlePeriodScheduling, | 230 if (m_mimeType == MimeTypeWebp) { |
199 const double& quality) { | 231 if (!isMainThread()) { |
200 ASSERT(isMainThread()); | 232 DCHECK(m_functionType == OffscreenCanvasToBlobPromise); |
| 233 // When OffscreenCanvas.convertToBlob() occurs on worker thread, |
| 234 // we do not need to use background task runner to reduce load on main. |
| 235 // So we just directly encode images on the worker thread. |
| 236 if (!ImageDataBuffer(m_size, m_data->data()) |
| 237 .encodeImage("image/webp", quality, m_encodedImage.get())) { |
| 238 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
| 239 ->postTask( |
| 240 BLINK_FROM_HERE, |
| 241 WTF::bind(&CanvasAsyncBlobCreator::createNullAndReturnResult, |
| 242 wrapPersistent(this))); |
201 | 243 |
202 if (canUseIdlePeriodScheduling) { | 244 return; |
| 245 } |
| 246 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
| 247 ->postTask( |
| 248 BLINK_FROM_HERE, |
| 249 WTF::bind(&CanvasAsyncBlobCreator::createBlobAndReturnResult, |
| 250 wrapPersistent(this))); |
| 251 |
| 252 } else { |
| 253 BackgroundTaskRunner::TaskSize taskSize = |
| 254 (m_size.height() * m_size.width() >= LongTaskImageSizeThreshold) |
| 255 ? BackgroundTaskRunner::TaskSizeLongRunningTask |
| 256 : BackgroundTaskRunner::TaskSizeShortRunningTask; |
| 257 BackgroundTaskRunner::postOnBackgroundThread( |
| 258 BLINK_FROM_HERE, |
| 259 crossThreadBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, |
| 260 wrapCrossThreadPersistent(this), quality), |
| 261 taskSize); |
| 262 } |
| 263 } else { |
203 m_idleTaskStatus = IdleTaskNotStarted; | 264 m_idleTaskStatus = IdleTaskNotStarted; |
204 if (m_mimeType == MimeTypePng) { | 265 if (m_mimeType == MimeTypePng) { |
205 this->scheduleInitiatePngEncoding(); | 266 this->scheduleInitiatePngEncoding(); |
206 } else if (m_mimeType == MimeTypeJpeg) { | 267 } else if (m_mimeType == MimeTypeJpeg) { |
207 this->scheduleInitiateJpegEncoding(quality); | 268 this->scheduleInitiateJpegEncoding(quality); |
208 } else { | 269 } else { |
209 // Progressive encoding is only applicable to png and jpeg image format, | 270 // Progressive encoding is only applicable to png and jpeg image format, |
210 // and thus idle tasks scheduling can only be applied to these image | 271 // and thus idle tasks scheduling can only be applied to these image |
211 // formats. | 272 // formats. |
212 // TODO(xlai): Progressive encoding on webp image formats | 273 // TODO(xlai): Progressive encoding on webp image formats |
213 // (crbug.com/571399) | 274 // (crbug.com/571399) |
214 ASSERT_NOT_REACHED(); | 275 NOTREACHED(); |
215 } | 276 } |
216 // We post the below task to check if the above idle task isn't late. | 277 |
217 // There's no risk of concurrency as both tasks are on main thread. | 278 // TODO: Enforce OffscreenCanvas.convertToBlob to finish within deadline. |
218 this->postDelayedTaskToMainThread( | 279 // See crbug.com/657102. |
219 BLINK_FROM_HERE, | 280 if (m_functionType == HTMLCanvasToBlobCallback) { |
220 WTF::bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent, | 281 // We post the below task to check if the above idle task isn't late. |
221 wrapPersistent(this), quality), | 282 // There's no risk of concurrency as both tasks are on main thread. |
222 IdleTaskStartTimeoutDelay); | 283 this->postDelayedTaskToMainThread( |
223 } else if (m_mimeType == MimeTypeWebp) { | 284 BLINK_FROM_HERE, |
224 BackgroundTaskRunner::TaskSize taskSize = | 285 WTF::bind(&CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent, |
225 (m_size.height() * m_size.width() >= LongTaskImageSizeThreshold) | 286 wrapPersistent(this), quality), |
226 ? BackgroundTaskRunner::TaskSizeLongRunningTask | 287 IdleTaskStartTimeoutDelay); |
227 : BackgroundTaskRunner::TaskSizeShortRunningTask; | 288 } |
228 BackgroundTaskRunner::postOnBackgroundThread( | |
229 BLINK_FROM_HERE, | |
230 crossThreadBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, | |
231 wrapCrossThreadPersistent(this), quality), | |
232 taskSize); | |
233 } | 289 } |
234 } | 290 } |
235 | 291 |
236 void CanvasAsyncBlobCreator::scheduleInitiateJpegEncoding( | 292 void CanvasAsyncBlobCreator::scheduleInitiateJpegEncoding( |
237 const double& quality) { | 293 const double& quality) { |
238 m_scheduleInitiateStartTime = WTF::monotonicallyIncreasingTime(); | 294 m_scheduleInitiateStartTime = WTF::monotonicallyIncreasingTime(); |
239 Platform::current()->mainThread()->scheduler()->postIdleTask( | 295 Platform::current()->currentThread()->scheduler()->postIdleTask( |
240 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::initiateJpegEncoding, | 296 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::initiateJpegEncoding, |
241 wrapPersistent(this), quality)); | 297 wrapPersistent(this), quality)); |
242 } | 298 } |
243 | 299 |
244 void CanvasAsyncBlobCreator::initiateJpegEncoding(const double& quality, | 300 void CanvasAsyncBlobCreator::initiateJpegEncoding(const double& quality, |
245 double deadlineSeconds) { | 301 double deadlineSeconds) { |
246 ASSERT(isMainThread()); | |
247 recordElapsedTimeHistogram( | 302 recordElapsedTimeHistogram( |
248 InitiateEncodingDelay, MimeTypeJpeg, | 303 m_functionType, InitiateEncodingDelay, MimeTypeJpeg, |
249 WTF::monotonicallyIncreasingTime() - m_scheduleInitiateStartTime); | 304 WTF::monotonicallyIncreasingTime() - m_scheduleInitiateStartTime); |
250 if (m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask) { | 305 if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) { |
251 return; | 306 return; |
252 } | 307 } |
253 | 308 |
254 ASSERT(m_idleTaskStatus == IdleTaskNotStarted); | 309 DCHECK(m_idleTaskStatus == IdleTaskNotStarted); |
255 m_idleTaskStatus = IdleTaskStarted; | 310 m_idleTaskStatus = IdleTaskStarted; |
256 | 311 |
257 if (!initializeJpegStruct(quality)) { | 312 if (!initializeJpegStruct(quality)) { |
258 m_idleTaskStatus = IdleTaskFailed; | 313 m_idleTaskStatus = IdleTaskFailed; |
259 return; | 314 return; |
260 } | 315 } |
261 this->idleEncodeRowsJpeg(deadlineSeconds); | 316 this->idleEncodeRowsJpeg(deadlineSeconds); |
262 } | 317 } |
263 | 318 |
264 void CanvasAsyncBlobCreator::scheduleInitiatePngEncoding() { | 319 void CanvasAsyncBlobCreator::scheduleInitiatePngEncoding() { |
265 m_scheduleInitiateStartTime = WTF::monotonicallyIncreasingTime(); | 320 m_scheduleInitiateStartTime = WTF::monotonicallyIncreasingTime(); |
266 Platform::current()->mainThread()->scheduler()->postIdleTask( | 321 Platform::current()->currentThread()->scheduler()->postIdleTask( |
267 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::initiatePngEncoding, | 322 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::initiatePngEncoding, |
268 wrapPersistent(this))); | 323 wrapPersistent(this))); |
269 } | 324 } |
270 | 325 |
271 void CanvasAsyncBlobCreator::initiatePngEncoding(double deadlineSeconds) { | 326 void CanvasAsyncBlobCreator::initiatePngEncoding(double deadlineSeconds) { |
272 ASSERT(isMainThread()); | |
273 recordElapsedTimeHistogram( | 327 recordElapsedTimeHistogram( |
274 InitiateEncodingDelay, MimeTypePng, | 328 m_functionType, InitiateEncodingDelay, MimeTypePng, |
275 WTF::monotonicallyIncreasingTime() - m_scheduleInitiateStartTime); | 329 WTF::monotonicallyIncreasingTime() - m_scheduleInitiateStartTime); |
276 | 330 if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) { |
277 if (m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask) { | |
278 return; | 331 return; |
279 } | 332 } |
280 | 333 |
281 ASSERT(m_idleTaskStatus == IdleTaskNotStarted); | 334 DCHECK(m_idleTaskStatus == IdleTaskNotStarted); |
282 m_idleTaskStatus = IdleTaskStarted; | 335 m_idleTaskStatus = IdleTaskStarted; |
283 | 336 |
284 if (!initializePngStruct()) { | 337 if (!initializePngStruct()) { |
285 m_idleTaskStatus = IdleTaskFailed; | 338 m_idleTaskStatus = IdleTaskFailed; |
286 return; | 339 return; |
287 } | 340 } |
288 this->idleEncodeRowsPng(deadlineSeconds); | 341 this->idleEncodeRowsPng(deadlineSeconds); |
289 } | 342 } |
290 | 343 |
291 void CanvasAsyncBlobCreator::idleEncodeRowsPng(double deadlineSeconds) { | 344 void CanvasAsyncBlobCreator::idleEncodeRowsPng(double deadlineSeconds) { |
292 ASSERT(isMainThread()); | 345 if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) { |
293 if (m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask) { | |
294 return; | 346 return; |
295 } | 347 } |
296 | 348 |
297 double startTime = WTF::monotonicallyIncreasingTime(); | 349 double startTime = WTF::monotonicallyIncreasingTime(); |
298 unsigned char* inputPixels = | 350 unsigned char* inputPixels = |
299 m_data->data() + m_pixelRowStride * m_numRowsCompleted; | 351 m_data->data() + m_pixelRowStride * m_numRowsCompleted; |
300 for (int y = m_numRowsCompleted; y < m_size.height(); ++y) { | 352 for (int y = m_numRowsCompleted; y < m_size.height(); ++y) { |
301 if (isDeadlineNearOrPassed(deadlineSeconds)) { | 353 if (isDeadlineNearOrPassed(deadlineSeconds)) { |
302 m_numRowsCompleted = y; | 354 m_numRowsCompleted = y; |
303 m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime); | 355 m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime); |
304 Platform::current()->currentThread()->scheduler()->postIdleTask( | 356 Platform::current()->currentThread()->scheduler()->postIdleTask( |
305 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::idleEncodeRowsPng, | 357 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::idleEncodeRowsPng, |
306 wrapPersistent(this))); | 358 wrapPersistent(this))); |
307 return; | 359 return; |
308 } | 360 } |
309 PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get()); | 361 PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get()); |
310 inputPixels += m_pixelRowStride; | 362 inputPixels += m_pixelRowStride; |
311 } | 363 } |
312 m_numRowsCompleted = m_size.height(); | 364 m_numRowsCompleted = m_size.height(); |
313 PNGImageEncoder::finalizePng(m_pngEncoderState.get()); | 365 PNGImageEncoder::finalizePng(m_pngEncoderState.get()); |
314 | 366 |
315 m_idleTaskStatus = IdleTaskCompleted; | 367 m_idleTaskStatus = IdleTaskCompleted; |
316 m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime); | 368 m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime); |
317 recordElapsedTimeHistogram(IdleEncodeDuration, MimeTypePng, m_elapsedTime); | 369 recordElapsedTimeHistogram(m_functionType, IdleEncodeDuration, MimeTypePng, |
| 370 m_elapsedTime); |
318 if (isDeadlineNearOrPassed(deadlineSeconds)) { | 371 if (isDeadlineNearOrPassed(deadlineSeconds)) { |
319 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 372 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
320 ->postTask( | 373 ->postTask(BLINK_FROM_HERE, |
321 BLINK_FROM_HERE, | 374 WTF::bind(&CanvasAsyncBlobCreator::createBlobAndReturnResult, |
322 WTF::bind(&CanvasAsyncBlobCreator::createBlobAndInvokeCallback, | 375 wrapPersistent(this))); |
323 wrapPersistent(this))); | |
324 } else { | 376 } else { |
325 this->createBlobAndInvokeCallback(); | 377 this->createBlobAndReturnResult(); |
326 } | 378 } |
327 } | 379 } |
328 | 380 |
329 void CanvasAsyncBlobCreator::idleEncodeRowsJpeg(double deadlineSeconds) { | 381 void CanvasAsyncBlobCreator::idleEncodeRowsJpeg(double deadlineSeconds) { |
330 ASSERT(isMainThread()); | 382 if (m_idleTaskStatus == IdleTaskSwitchedToImmediateTask) { |
331 if (m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask) { | |
332 return; | 383 return; |
333 } | 384 } |
334 | 385 |
335 double startTime = WTF::monotonicallyIncreasingTime(); | 386 double startTime = WTF::monotonicallyIncreasingTime(); |
336 m_numRowsCompleted = JPEGImageEncoder::progressiveEncodeRowsJpegHelper( | 387 m_numRowsCompleted = JPEGImageEncoder::progressiveEncodeRowsJpegHelper( |
337 m_jpegEncoderState.get(), m_data->data(), m_numRowsCompleted, | 388 m_jpegEncoderState.get(), m_data->data(), m_numRowsCompleted, |
338 SlackBeforeDeadline, deadlineSeconds); | 389 SlackBeforeDeadline, deadlineSeconds); |
339 m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime); | 390 m_elapsedTime += (WTF::monotonicallyIncreasingTime() - startTime); |
340 if (m_numRowsCompleted == m_size.height()) { | 391 if (m_numRowsCompleted == m_size.height()) { |
341 m_idleTaskStatus = IdleTaskCompleted; | 392 m_idleTaskStatus = IdleTaskCompleted; |
342 recordElapsedTimeHistogram(IdleEncodeDuration, MimeTypeJpeg, m_elapsedTime); | 393 recordElapsedTimeHistogram(m_functionType, IdleEncodeDuration, MimeTypeJpeg, |
| 394 m_elapsedTime); |
343 | 395 |
344 if (isDeadlineNearOrPassed(deadlineSeconds)) { | 396 if (isDeadlineNearOrPassed(deadlineSeconds)) { |
345 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 397 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
346 ->postTask( | 398 ->postTask( |
347 BLINK_FROM_HERE, | 399 BLINK_FROM_HERE, |
348 WTF::bind(&CanvasAsyncBlobCreator::createBlobAndInvokeCallback, | 400 WTF::bind(&CanvasAsyncBlobCreator::createBlobAndReturnResult, |
349 wrapPersistent(this))); | 401 wrapPersistent(this))); |
350 } else { | 402 } else { |
351 this->createBlobAndInvokeCallback(); | 403 this->createBlobAndReturnResult(); |
352 } | 404 } |
353 } else if (m_numRowsCompleted == JPEGImageEncoder::ProgressiveEncodeFailed) { | 405 } else if (m_numRowsCompleted == JPEGImageEncoder::ProgressiveEncodeFailed) { |
354 m_idleTaskStatus = IdleTaskFailed; | 406 m_idleTaskStatus = IdleTaskFailed; |
355 this->createNullAndInvokeCallback(); | 407 this->createNullAndReturnResult(); |
356 } else { | 408 } else { |
357 Platform::current()->currentThread()->scheduler()->postIdleTask( | 409 Platform::current()->currentThread()->scheduler()->postIdleTask( |
358 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::idleEncodeRowsJpeg, | 410 BLINK_FROM_HERE, WTF::bind(&CanvasAsyncBlobCreator::idleEncodeRowsJpeg, |
359 wrapPersistent(this))); | 411 wrapPersistent(this))); |
360 } | 412 } |
361 } | 413 } |
362 | 414 |
363 void CanvasAsyncBlobCreator::encodeRowsPngOnMainThread() { | 415 void CanvasAsyncBlobCreator::encodeRowsPngOnMainThread() { |
364 ASSERT(m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask); | 416 DCHECK(m_idleTaskStatus == IdleTaskSwitchedToImmediateTask); |
365 | 417 |
366 // Continue encoding from the last completed row | 418 // Continue encoding from the last completed row |
367 unsigned char* inputPixels = | 419 unsigned char* inputPixels = |
368 m_data->data() + m_pixelRowStride * m_numRowsCompleted; | 420 m_data->data() + m_pixelRowStride * m_numRowsCompleted; |
369 for (int y = m_numRowsCompleted; y < m_size.height(); ++y) { | 421 for (int y = m_numRowsCompleted; y < m_size.height(); ++y) { |
370 PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get()); | 422 PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get()); |
371 inputPixels += m_pixelRowStride; | 423 inputPixels += m_pixelRowStride; |
372 } | 424 } |
373 PNGImageEncoder::finalizePng(m_pngEncoderState.get()); | 425 PNGImageEncoder::finalizePng(m_pngEncoderState.get()); |
374 this->createBlobAndInvokeCallback(); | 426 this->createBlobAndReturnResult(); |
375 | 427 |
376 this->signalAlternativeCodePathFinishedForTesting(); | 428 this->signalAlternativeCodePathFinishedForTesting(); |
377 } | 429 } |
378 | 430 |
379 void CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread() { | 431 void CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread() { |
380 ASSERT(m_idleTaskStatus == IdleTaskSwitchedToMainThreadTask); | 432 DCHECK(m_idleTaskStatus == IdleTaskSwitchedToImmediateTask); |
381 | 433 |
382 // Continue encoding from the last completed row | 434 // Continue encoding from the last completed row |
383 if (JPEGImageEncoder::encodeWithPreInitializedState( | 435 if (JPEGImageEncoder::encodeWithPreInitializedState( |
384 std::move(m_jpegEncoderState), m_data->data(), m_numRowsCompleted)) { | 436 std::move(m_jpegEncoderState), m_data->data(), m_numRowsCompleted)) { |
385 this->createBlobAndInvokeCallback(); | 437 this->createBlobAndReturnResult(); |
386 } else { | 438 } else { |
387 this->createNullAndInvokeCallback(); | 439 this->createNullAndReturnResult(); |
388 } | 440 } |
389 | 441 |
390 this->signalAlternativeCodePathFinishedForTesting(); | 442 this->signalAlternativeCodePathFinishedForTesting(); |
391 } | 443 } |
392 | 444 |
393 void CanvasAsyncBlobCreator::createBlobAndInvokeCallback() { | 445 void CanvasAsyncBlobCreator::createBlobAndReturnResult() { |
394 ASSERT(isMainThread()); | 446 recordIdleTaskStatusHistogram(m_functionType, m_idleTaskStatus); |
395 recordIdleTaskStatusHistogram(m_idleTaskStatus); | 447 recordElapsedTimeHistogram(m_functionType, ToBlobDuration, m_mimeType, |
| 448 WTF::monotonicallyIncreasingTime() - m_startTime); |
396 | 449 |
397 recordElapsedTimeHistogram(ToBlobDuration, m_mimeType, | |
398 WTF::monotonicallyIncreasingTime() - m_startTime); | |
399 Blob* resultBlob = | 450 Blob* resultBlob = |
400 Blob::create(m_encodedImage->data(), m_encodedImage->size(), | 451 Blob::create(m_encodedImage->data(), m_encodedImage->size(), |
401 convertMimeTypeEnumToString(m_mimeType)); | 452 convertMimeTypeEnumToString(m_mimeType)); |
402 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 453 if (m_functionType == HTMLCanvasToBlobCallback) { |
403 ->postTask(BLINK_FROM_HERE, WTF::bind(&BlobCallback::handleEvent, | 454 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
404 wrapPersistent(m_callback.get()), | 455 ->postTask(BLINK_FROM_HERE, WTF::bind(&BlobCallback::handleEvent, |
405 wrapPersistent(resultBlob))); | 456 wrapPersistent(m_callback.get()), |
| 457 wrapPersistent(resultBlob))); |
| 458 } else { |
| 459 m_scriptPromiseResolver->resolve(resultBlob); |
| 460 } |
406 // Avoid unwanted retention, see dispose(). | 461 // Avoid unwanted retention, see dispose(). |
407 dispose(); | 462 dispose(); |
408 } | 463 } |
409 | 464 |
410 void CanvasAsyncBlobCreator::createNullAndInvokeCallback() { | 465 void CanvasAsyncBlobCreator::createNullAndReturnResult() { |
411 ASSERT(isMainThread()); | 466 recordIdleTaskStatusHistogram(m_functionType, m_idleTaskStatus); |
412 recordIdleTaskStatusHistogram(m_idleTaskStatus); | 467 if (m_functionType == HTMLCanvasToBlobCallback) { |
413 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 468 DCHECK(isMainThread()); |
414 ->postTask(BLINK_FROM_HERE, | 469 recordIdleTaskStatusHistogram(m_functionType, m_idleTaskStatus); |
415 WTF::bind(&BlobCallback::handleEvent, | 470 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
416 wrapPersistent(m_callback.get()), nullptr)); | 471 ->postTask(BLINK_FROM_HERE, |
| 472 WTF::bind(&BlobCallback::handleEvent, |
| 473 wrapPersistent(m_callback.get()), nullptr)); |
| 474 } else { |
| 475 Blob* blob = nullptr; |
| 476 m_scriptPromiseResolver->reject(blob); |
| 477 } |
417 // Avoid unwanted retention, see dispose(). | 478 // Avoid unwanted retention, see dispose(). |
418 dispose(); | 479 dispose(); |
419 } | 480 } |
420 | 481 |
421 void CanvasAsyncBlobCreator::encodeImageOnEncoderThread(double quality) { | 482 void CanvasAsyncBlobCreator::encodeImageOnEncoderThread(double quality) { |
422 ASSERT(!isMainThread()); | 483 DCHECK(!isMainThread()); |
423 ASSERT(m_mimeType == MimeTypeWebp); | 484 DCHECK(m_mimeType == MimeTypeWebp); |
424 | 485 |
425 if (!ImageDataBuffer(m_size, m_data->data()) | 486 if (!ImageDataBuffer(m_size, m_data->data()) |
426 .encodeImage("image/webp", quality, m_encodedImage.get())) { | 487 .encodeImage("image/webp", quality, m_encodedImage.get())) { |
427 m_parentFrameTaskRunner->get(TaskType::CanvasBlobSerialization) | 488 m_parentFrameTaskRunner->get(TaskType::CanvasBlobSerialization) |
428 ->postTask(BLINK_FROM_HERE, | 489 ->postTask(BLINK_FROM_HERE, |
429 crossThreadBind(&BlobCallback::handleEvent, | 490 crossThreadBind(&BlobCallback::handleEvent, |
430 wrapCrossThreadPersistent(m_callback.get()), | 491 wrapCrossThreadPersistent(m_callback.get()), |
431 nullptr)); | 492 nullptr)); |
432 return; | 493 return; |
433 } | 494 } |
434 | 495 |
435 m_parentFrameTaskRunner->get(TaskType::CanvasBlobSerialization) | 496 m_parentFrameTaskRunner->get(TaskType::CanvasBlobSerialization) |
436 ->postTask( | 497 ->postTask( |
437 BLINK_FROM_HERE, | 498 BLINK_FROM_HERE, |
438 crossThreadBind(&CanvasAsyncBlobCreator::createBlobAndInvokeCallback, | 499 crossThreadBind(&CanvasAsyncBlobCreator::createBlobAndReturnResult, |
439 wrapCrossThreadPersistent(this))); | 500 wrapCrossThreadPersistent(this))); |
440 } | 501 } |
441 | 502 |
442 bool CanvasAsyncBlobCreator::initializePngStruct() { | 503 bool CanvasAsyncBlobCreator::initializePngStruct() { |
443 m_pngEncoderState = | 504 m_pngEncoderState = |
444 PNGImageEncoderState::create(m_size, m_encodedImage.get()); | 505 PNGImageEncoderState::create(m_size, m_encodedImage.get()); |
445 if (!m_pngEncoderState) { | 506 if (!m_pngEncoderState) { |
446 this->createNullAndInvokeCallback(); | 507 this->createNullAndReturnResult(); |
447 return false; | 508 return false; |
448 } | 509 } |
449 return true; | 510 return true; |
450 } | 511 } |
451 | 512 |
452 bool CanvasAsyncBlobCreator::initializeJpegStruct(double quality) { | 513 bool CanvasAsyncBlobCreator::initializeJpegStruct(double quality) { |
453 m_jpegEncoderState = | 514 m_jpegEncoderState = |
454 JPEGImageEncoderState::create(m_size, quality, m_encodedImage.get()); | 515 JPEGImageEncoderState::create(m_size, quality, m_encodedImage.get()); |
455 if (!m_jpegEncoderState) { | 516 if (!m_jpegEncoderState) { |
456 this->createNullAndInvokeCallback(); | 517 this->createNullAndReturnResult(); |
457 return false; | 518 return false; |
458 } | 519 } |
459 return true; | 520 return true; |
460 } | 521 } |
461 | 522 |
462 void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) { | 523 void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) { |
463 if (m_idleTaskStatus == IdleTaskStarted) { | 524 if (m_idleTaskStatus == IdleTaskStarted) { |
464 // Even if the task started quickly, we still want to ensure completion | 525 // Even if the task started quickly, we still want to ensure completion |
465 this->postDelayedTaskToMainThread( | 526 this->postDelayedTaskToMainThread( |
466 BLINK_FROM_HERE, | 527 BLINK_FROM_HERE, |
467 WTF::bind(&CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent, | 528 WTF::bind(&CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent, |
468 wrapPersistent(this)), | 529 wrapPersistent(this)), |
469 IdleTaskCompleteTimeoutDelay); | 530 IdleTaskCompleteTimeoutDelay); |
470 } else if (m_idleTaskStatus == IdleTaskNotStarted) { | 531 } else if (m_idleTaskStatus == IdleTaskNotStarted) { |
471 // If the idle task does not start after a delay threshold, we will | 532 // If the idle task does not start after a delay threshold, we will |
472 // force it to happen on main thread (even though it may cause more | 533 // force it to happen on main thread (even though it may cause more |
473 // janks) to prevent toBlob being postponed forever in extreme cases. | 534 // janks) to prevent toBlob being postponed forever in extreme cases. |
474 m_idleTaskStatus = IdleTaskSwitchedToMainThreadTask; | 535 m_idleTaskStatus = IdleTaskSwitchedToImmediateTask; |
475 signalTaskSwitchInStartTimeoutEventForTesting(); | 536 signalTaskSwitchInStartTimeoutEventForTesting(); |
476 | 537 |
477 if (m_mimeType == MimeTypePng) { | 538 if (m_mimeType == MimeTypePng) { |
478 if (initializePngStruct()) { | 539 if (initializePngStruct()) { |
479 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 540 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
480 ->postTask( | 541 ->postTask( |
481 BLINK_FROM_HERE, | 542 BLINK_FROM_HERE, |
482 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, | 543 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, |
483 wrapPersistent(this))); | 544 wrapPersistent(this))); |
484 } else { | 545 } else { |
485 // Failing in initialization of png struct | 546 // Failing in initialization of png struct |
486 this->signalAlternativeCodePathFinishedForTesting(); | 547 this->signalAlternativeCodePathFinishedForTesting(); |
487 } | 548 } |
488 } else { | 549 } else { |
489 ASSERT(m_mimeType == MimeTypeJpeg); | 550 DCHECK(m_mimeType == MimeTypeJpeg); |
490 if (initializeJpegStruct(quality)) { | 551 if (initializeJpegStruct(quality)) { |
491 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 552 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
492 ->postTask( | 553 ->postTask( |
493 BLINK_FROM_HERE, | 554 BLINK_FROM_HERE, |
494 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, | 555 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, |
495 wrapPersistent(this))); | 556 wrapPersistent(this))); |
496 } else { | 557 } else { |
497 // Failing in initialization of jpeg struct | 558 // Failing in initialization of jpeg struct |
498 this->signalAlternativeCodePathFinishedForTesting(); | 559 this->signalAlternativeCodePathFinishedForTesting(); |
499 } | 560 } |
500 } | 561 } |
501 } else { | 562 } else { |
502 ASSERT(m_idleTaskStatus == IdleTaskFailed || | 563 DCHECK(m_idleTaskStatus == IdleTaskFailed || |
503 m_idleTaskStatus == IdleTaskCompleted); | 564 m_idleTaskStatus == IdleTaskCompleted); |
504 this->signalAlternativeCodePathFinishedForTesting(); | 565 this->signalAlternativeCodePathFinishedForTesting(); |
505 } | 566 } |
506 } | 567 } |
507 | 568 |
508 void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent() { | 569 void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent() { |
509 ASSERT(m_idleTaskStatus != IdleTaskNotStarted); | 570 DCHECK(m_idleTaskStatus != IdleTaskNotStarted); |
510 | 571 |
511 if (m_idleTaskStatus == IdleTaskStarted) { | 572 if (m_idleTaskStatus == IdleTaskStarted) { |
512 // It has taken too long to complete for the idle task. | 573 // It has taken too long to complete for the idle task. |
513 m_idleTaskStatus = IdleTaskSwitchedToMainThreadTask; | 574 m_idleTaskStatus = IdleTaskSwitchedToImmediateTask; |
514 signalTaskSwitchInCompleteTimeoutEventForTesting(); | 575 signalTaskSwitchInCompleteTimeoutEventForTesting(); |
515 | 576 |
516 if (m_mimeType == MimeTypePng) { | 577 if (m_mimeType == MimeTypePng) { |
517 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 578 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
518 ->postTask( | 579 ->postTask( |
519 BLINK_FROM_HERE, | 580 BLINK_FROM_HERE, |
520 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, | 581 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, |
521 wrapPersistent(this))); | 582 wrapPersistent(this))); |
522 } else { | 583 } else { |
523 ASSERT(m_mimeType == MimeTypeJpeg); | 584 DCHECK(m_mimeType == MimeTypeJpeg); |
524 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 585 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
525 ->postTask( | 586 ->postTask( |
526 BLINK_FROM_HERE, | 587 BLINK_FROM_HERE, |
527 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, | 588 WTF::bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, |
528 wrapPersistent(this))); | 589 wrapPersistent(this))); |
529 } | 590 } |
530 } else { | 591 } else { |
531 ASSERT(m_idleTaskStatus == IdleTaskFailed || | 592 DCHECK(m_idleTaskStatus == IdleTaskFailed || |
532 m_idleTaskStatus == IdleTaskCompleted); | 593 m_idleTaskStatus == IdleTaskCompleted); |
533 this->signalAlternativeCodePathFinishedForTesting(); | 594 this->signalAlternativeCodePathFinishedForTesting(); |
534 } | 595 } |
535 } | 596 } |
536 | 597 |
537 void CanvasAsyncBlobCreator::postDelayedTaskToMainThread( | 598 void CanvasAsyncBlobCreator::postDelayedTaskToMainThread( |
538 const WebTraceLocation& location, | 599 const WebTraceLocation& location, |
539 std::unique_ptr<WTF::Closure> task, | 600 std::unique_ptr<WTF::Closure> task, |
540 double delayMs) { | 601 double delayMs) { |
541 DCHECK(isMainThread()); | 602 DCHECK(isMainThread()); |
542 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) | 603 TaskRunnerHelper::get(TaskType::CanvasBlobSerialization, m_document) |
543 ->postDelayedTask(location, std::move(task), delayMs); | 604 ->postDelayedTask(location, std::move(task), delayMs); |
544 } | 605 } |
545 | 606 |
546 DEFINE_TRACE(CanvasAsyncBlobCreator) { | 607 DEFINE_TRACE(CanvasAsyncBlobCreator) { |
547 visitor->trace(m_document); | 608 visitor->trace(m_document); |
548 visitor->trace(m_data); | 609 visitor->trace(m_data); |
549 visitor->trace(m_callback); | 610 visitor->trace(m_callback); |
550 visitor->trace(m_parentFrameTaskRunner); | 611 visitor->trace(m_parentFrameTaskRunner); |
| 612 visitor->trace(m_scriptPromiseResolver); |
551 } | 613 } |
552 | 614 |
553 } // namespace blink | 615 } // namespace blink |
OLD | NEW |