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/fileapi/Blob.h" | 7 #include "core/fileapi/Blob.h" |
8 #include "platform/Histogram.h" | |
8 #include "platform/ThreadSafeFunctional.h" | 9 #include "platform/ThreadSafeFunctional.h" |
9 #include "platform/graphics/ImageBuffer.h" | 10 #include "platform/graphics/ImageBuffer.h" |
10 #include "platform/image-encoders/JPEGImageEncoder.h" | 11 #include "platform/image-encoders/JPEGImageEncoder.h" |
11 #include "platform/image-encoders/PNGImageEncoder.h" | 12 #include "platform/image-encoders/PNGImageEncoder.h" |
12 #include "platform/threading/BackgroundTaskRunner.h" | 13 #include "platform/threading/BackgroundTaskRunner.h" |
13 #include "public/platform/Platform.h" | 14 #include "public/platform/Platform.h" |
14 #include "public/platform/WebScheduler.h" | 15 #include "public/platform/WebScheduler.h" |
15 #include "public/platform/WebTaskRunner.h" | 16 #include "public/platform/WebTaskRunner.h" |
16 #include "public/platform/WebThread.h" | 17 #include "public/platform/WebThread.h" |
17 #include "public/platform/WebTraceLocation.h" | 18 #include "public/platform/WebTraceLocation.h" |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
71 } else if (mimeType == "image/webp") { | 72 } else if (mimeType == "image/webp") { |
72 mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeWebp; | 73 mimeTypeEnum = CanvasAsyncBlobCreator::MimeTypeWebp; |
73 } else { | 74 } else { |
74 mimeTypeEnum = CanvasAsyncBlobCreator::NumberOfMimeTypeSupported; | 75 mimeTypeEnum = CanvasAsyncBlobCreator::NumberOfMimeTypeSupported; |
75 } | 76 } |
76 return mimeTypeEnum; | 77 return mimeTypeEnum; |
77 } | 78 } |
78 | 79 |
79 } // anonymous namespace | 80 } // anonymous namespace |
80 | 81 |
81 CanvasAsyncBlobCreator* CanvasAsyncBlobCreator::create(DOMUint8ClampedArray* unp remultipliedRGBAImageData, const String& mimeType, const IntSize& size, BlobCall back* callback) | 82 CanvasAsyncBlobCreator* CanvasAsyncBlobCreator::create(DOMUint8ClampedArray* unp remultipliedRGBAImageData, const String& mimeType, const IntSize& size, BlobCall back* callback, double startTime) |
82 { | 83 { |
83 return new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData, convertMimeT ypeStringToEnum(mimeType), size, callback); | 84 return new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData, convertMimeT ypeStringToEnum(mimeType), size, callback, startTime); |
84 } | 85 } |
85 | 86 |
86 CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data, MimeT ype mimeType, const IntSize& size, BlobCallback* callback) | 87 CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data, MimeT ype mimeType, const IntSize& size, BlobCallback* callback, double startTime) |
87 : m_data(data) | 88 : m_data(data) |
88 , m_size(size) | 89 , m_size(size) |
89 , m_mimeType(mimeType) | 90 , m_mimeType(mimeType) |
90 , m_callback(callback) | 91 , m_callback(callback) |
92 , m_startTime(startTime) | |
91 { | 93 { |
92 ASSERT(m_data->length() == (unsigned) (size.height() * size.width() * 4)); | 94 ASSERT(m_data->length() == (unsigned) (size.height() * size.width() * 4)); |
93 m_encodedImage = adoptPtr(new Vector<unsigned char>()); | 95 m_encodedImage = adoptPtr(new Vector<unsigned char>()); |
94 m_pixelRowStride = size.width() * NumChannelsPng; | 96 m_pixelRowStride = size.width() * NumChannelsPng; |
95 m_idleTaskStatus = IdleTaskNotSupported; | 97 m_idleTaskStatus = IdleTaskNotSupported; |
96 m_numRowsCompleted = 0; | 98 m_numRowsCompleted = 0; |
97 } | 99 } |
98 | 100 |
99 CanvasAsyncBlobCreator::~CanvasAsyncBlobCreator() | 101 CanvasAsyncBlobCreator::~CanvasAsyncBlobCreator() |
100 { | 102 { |
(...skipping 10 matching lines...) Expand all Loading... | |
111 } else if (m_mimeType == MimeTypeJpeg) { | 113 } else if (m_mimeType == MimeTypeJpeg) { |
112 this->scheduleInitiateJpegEncoding(quality); | 114 this->scheduleInitiateJpegEncoding(quality); |
113 } else { | 115 } else { |
114 // Progressive encoding is only applicable to png and jpeg image for mat, | 116 // Progressive encoding is only applicable to png and jpeg image for mat, |
115 // and thus idle tasks scheduling can only be applied to these image formats. | 117 // and thus idle tasks scheduling can only be applied to these image formats. |
116 // TODO(xlai): Progressive encoding on webp image formats (crbug.com /571399) | 118 // TODO(xlai): Progressive encoding on webp image formats (crbug.com /571399) |
117 ASSERT_NOT_REACHED(); | 119 ASSERT_NOT_REACHED(); |
118 } | 120 } |
119 // We post the below task to check if the above idle task isn't late. | 121 // We post the below task to check if the above idle task isn't late. |
120 // There's no risk of concurrency as both tasks are on main thread. | 122 // There's no risk of concurrency as both tasks are on main thread. |
121 this->postDelayedTaskToMainThread(BLINK_FROM_HERE, bind(&CanvasAsyncBlob Creator::idleTaskStartTimeoutEvent, this, quality), IdleTaskStartTimeoutDelay); | 123 double startTime = WTF::monotonicallyIncreasingTime(); |
124 this->postDelayedTaskToMainThread(BLINK_FROM_HERE, bind(&CanvasAsyncBlob Creator::idleTaskStartTimeoutEvent, this, quality, startTime), IdleTaskStartTime outDelay); | |
122 } else if (m_mimeType == MimeTypeWebp) { | 125 } else if (m_mimeType == MimeTypeWebp) { |
123 BackgroundTaskRunner::TaskSize taskSize = (m_size.height() * m_size.widt h() >= LongTaskImageSizeThreshold) ? BackgroundTaskRunner::TaskSizeLongRunningTa sk : BackgroundTaskRunner::TaskSizeShortRunningTask; | 126 BackgroundTaskRunner::TaskSize taskSize = (m_size.height() * m_size.widt h() >= LongTaskImageSizeThreshold) ? BackgroundTaskRunner::TaskSizeLongRunningTa sk : BackgroundTaskRunner::TaskSizeShortRunningTask; |
124 BackgroundTaskRunner::postOnBackgroundThread(BLINK_FROM_HERE, threadSafe Bind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, wrapCrossThreadPersist ent(this), quality), taskSize); | 127 BackgroundTaskRunner::postOnBackgroundThread(BLINK_FROM_HERE, threadSafe Bind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, wrapCrossThreadPersist ent(this), quality), taskSize); |
125 } | 128 } |
126 } | 129 } |
127 | 130 |
128 void CanvasAsyncBlobCreator::scheduleInitiateJpegEncoding(const double& quality) | 131 void CanvasAsyncBlobCreator::scheduleInitiateJpegEncoding(const double& quality) |
129 { | 132 { |
130 Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE , bind<double>(&CanvasAsyncBlobCreator::initiateJpegEncoding, this, quality)); | 133 Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE , bind<double>(&CanvasAsyncBlobCreator::initiateJpegEncoding, this, quality)); |
131 } | 134 } |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
253 | 256 |
254 void CanvasAsyncBlobCreator::createBlobAndInvokeCallback() | 257 void CanvasAsyncBlobCreator::createBlobAndInvokeCallback() |
255 { | 258 { |
256 ASSERT(isMainThread()); | 259 ASSERT(isMainThread()); |
257 Blob* resultBlob = Blob::create(m_encodedImage->data(), m_encodedImage->size (), convertMimeTypeEnumToString(m_mimeType)); | 260 Blob* resultBlob = Blob::create(m_encodedImage->data(), m_encodedImage->size (), convertMimeTypeEnumToString(m_mimeType)); |
258 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_H ERE, bind(&BlobCallback::handleEvent, m_callback, resultBlob)); | 261 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_H ERE, bind(&BlobCallback::handleEvent, m_callback, resultBlob)); |
259 // Since toBlob is done, timeout events are no longer needed. So we clear | 262 // Since toBlob is done, timeout events are no longer needed. So we clear |
260 // non-GC members to allow teardown of CanvasAsyncBlobCreator. | 263 // non-GC members to allow teardown of CanvasAsyncBlobCreator. |
261 m_data.clear(); | 264 m_data.clear(); |
262 m_callback.clear(); | 265 m_callback.clear(); |
266 double elapsedTime = WTF::monotonicallyIncreasingTime() - m_startTime; | |
267 if (m_mimeType == MimeTypePng) { | |
268 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobPNGCounter, new CustomCountHistogram("Blink.Canvas.ToBlob.PNG", 0, 10000000, 50)); | |
269 toBlobPNGCounter.count(elapsedTime * 1000000.0); | |
270 } else if (m_mimeType == MimeTypeJpeg) { | |
271 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobJPEGCounter, new CustomCountHistogram("Blink.Canvas.ToBlob.JPEG", 0, 10000000, 50)); | |
272 toBlobJPEGCounter.count(elapsedTime * 1000000.0); | |
273 } else { | |
274 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobWEBPCounter, new CustomCountHistogram("Blink.Canvas.ToBlob.WEBP", 0, 10000000, 50)); | |
275 toBlobWEBPCounter.count(elapsedTime * 1000000.0); | |
276 } | |
263 } | 277 } |
264 | 278 |
265 void CanvasAsyncBlobCreator::createNullAndInvokeCallback() | 279 void CanvasAsyncBlobCreator::createNullAndInvokeCallback() |
266 { | 280 { |
267 ASSERT(isMainThread()); | 281 ASSERT(isMainThread()); |
268 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_H ERE, bind(&BlobCallback::handleEvent, m_callback, nullptr)); | 282 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLINK_FROM_H ERE, bind(&BlobCallback::handleEvent, m_callback, nullptr)); |
269 // Since toBlob is done (failed), timeout events are no longer needed. So we | 283 // Since toBlob is done (failed), timeout events are no longer needed. So we |
270 // clear non-GC members to allow teardown of CanvasAsyncBlobCreator. | 284 // clear non-GC members to allow teardown of CanvasAsyncBlobCreator. |
271 m_data.clear(); | 285 m_data.clear(); |
272 m_callback.clear(); | 286 m_callback.clear(); |
(...skipping 25 matching lines...) Expand all Loading... | |
298 bool CanvasAsyncBlobCreator::initializeJpegStruct(double quality) | 312 bool CanvasAsyncBlobCreator::initializeJpegStruct(double quality) |
299 { | 313 { |
300 m_jpegEncoderState = JPEGImageEncoderState::create(m_size, quality, m_encode dImage.get()); | 314 m_jpegEncoderState = JPEGImageEncoderState::create(m_size, quality, m_encode dImage.get()); |
301 if (!m_jpegEncoderState) { | 315 if (!m_jpegEncoderState) { |
302 this->createNullAndInvokeCallback(); | 316 this->createNullAndInvokeCallback(); |
303 return false; | 317 return false; |
304 } | 318 } |
305 return true; | 319 return true; |
306 } | 320 } |
307 | 321 |
308 void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality) | 322 void CanvasAsyncBlobCreator::recordTimeoutSwitch(IdleTaskTimeoutType type) |
309 { | 323 { |
324 if (m_mimeType == MimeTypePng) { | |
325 DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, idleTaskTimeoutSwi tchPNG, new EnumerationHistogram("Canvas.ToBlob.IdleTaskTimeoutSwitch.PNG", Idle TaskTimeoutSupported)); | |
326 idleTaskTimeoutSwitchPNG.count(type); | |
327 } else { | |
328 DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, idleTaskTimeoutSwi tchJPEG, new EnumerationHistogram("Canvas.ToBlob.IdleTaskTimeoutSwitch.JPEG", Id leTaskTimeoutSupported)); | |
329 idleTaskTimeoutSwitchJPEG.count(type); | |
330 } | |
331 } | |
332 | |
333 void CanvasAsyncBlobCreator::idleTaskStartTimeoutEvent(double quality, double st artTime) | |
334 { | |
335 double elapsedTime = WTF::monotonicallyIncreasingTime() - startTime; | |
336 if (m_mimeType == MimeTypePng) { | |
337 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobPNGStartTime outCounter, new CustomCountHistogram("Blink.Canvas.ToBlobTimeoutDuration.PNG.Sta rt", 0, 10000000, 50)); | |
338 toBlobPNGStartTimeoutCounter.count(elapsedTime * 1000000.0); | |
339 } else if (m_mimeType == MimeTypeJpeg) { | |
340 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobJPEGStartTim eoutCounter, new CustomCountHistogram("Blink.Canvas.ToBlobTimeoutDuration.JPEG.S tart", 0, 10000000, 50)); | |
341 toBlobJPEGStartTimeoutCounter.count(elapsedTime * 1000000.0); | |
342 } else { | |
343 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobWEBPStartTim eoutCounter, new CustomCountHistogram("Blink.Canvas.ToBlobTimeoutDuration.WEBP.S tart", 0, 10000000, 50)); | |
344 toBlobWEBPStartTimeoutCounter.count(elapsedTime * 1000000.0); | |
345 } | |
xidachen
2016/06/07 20:46:29
Is this recording meaningful now that we change to
| |
346 | |
310 if (m_idleTaskStatus == IdleTaskStarted) { | 347 if (m_idleTaskStatus == IdleTaskStarted) { |
311 // Even if the task started quickly, we still want to ensure completion | 348 // Even if the task started quickly, we still want to ensure completion |
312 this->postDelayedTaskToMainThread(BLINK_FROM_HERE, bind(&CanvasAsyncBlob Creator::idleTaskCompleteTimeoutEvent, this), IdleTaskCompleteTimeoutDelay); | 349 startTime = WTF::monotonicallyIncreasingTime(); |
350 this->postDelayedTaskToMainThread(BLINK_FROM_HERE, bind(&CanvasAsyncBlob Creator::idleTaskCompleteTimeoutEvent, this, startTime), IdleTaskCompleteTimeout Delay); | |
313 } else if (m_idleTaskStatus == IdleTaskNotStarted) { | 351 } else if (m_idleTaskStatus == IdleTaskNotStarted) { |
314 // If the idle task does not start after a delay threshold, we will | 352 // If the idle task does not start after a delay threshold, we will |
315 // force it to happen on main thread (even though it may cause more | 353 // force it to happen on main thread (even though it may cause more |
316 // janks) to prevent toBlob being postponed forever in extreme cases. | 354 // janks) to prevent toBlob being postponed forever in extreme cases. |
317 m_idleTaskStatus = IdleTaskSwitchedToMainThreadTask; | 355 m_idleTaskStatus = IdleTaskSwitchedToMainThreadTask; |
318 signalTaskSwitchInStartTimeoutEventForTesting(); | 356 signalTaskSwitchInStartTimeoutEventForTesting(); |
319 | 357 |
320 if (m_mimeType == MimeTypePng) { | 358 if (m_mimeType == MimeTypePng) { |
321 if (initializePngStruct()) { | 359 if (initializePngStruct()) { |
360 recordTimeoutSwitch(IdleTaskStartTimeout); | |
322 Platform::current()->mainThread()->getWebTaskRunner()->postTask( BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this)) ; | 361 Platform::current()->mainThread()->getWebTaskRunner()->postTask( BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this)) ; |
323 } else { | 362 } else { |
324 // Failing in initialization of png struct | 363 // Failing in initialization of png struct |
325 this->signalAlternativeCodePathFinishedForTesting(); | 364 this->signalAlternativeCodePathFinishedForTesting(); |
326 } | 365 } |
327 } else { | 366 } else { |
328 ASSERT(m_mimeType == MimeTypeJpeg); | 367 ASSERT(m_mimeType == MimeTypeJpeg); |
329 if (initializeJpegStruct(quality)) { | 368 if (initializeJpegStruct(quality)) { |
369 recordTimeoutSwitch(IdleTaskStartTimeout); | |
330 Platform::current()->mainThread()->getWebTaskRunner()->postTask( BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, this) ); | 370 Platform::current()->mainThread()->getWebTaskRunner()->postTask( BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, this) ); |
331 } else { | 371 } else { |
332 // Failing in initialization of jpeg struct | 372 // Failing in initialization of jpeg struct |
333 this->signalAlternativeCodePathFinishedForTesting(); | 373 this->signalAlternativeCodePathFinishedForTesting(); |
334 } | 374 } |
335 } | 375 } |
336 } else { | 376 } else { |
337 ASSERT(m_idleTaskStatus == IdleTaskFailed || m_idleTaskStatus == IdleTas kCompleted); | 377 ASSERT(m_idleTaskStatus == IdleTaskFailed || m_idleTaskStatus == IdleTas kCompleted); |
338 this->signalAlternativeCodePathFinishedForTesting(); | 378 this->signalAlternativeCodePathFinishedForTesting(); |
339 } | 379 } |
340 | 380 |
341 } | 381 } |
342 | 382 |
343 void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent() | 383 void CanvasAsyncBlobCreator::idleTaskCompleteTimeoutEvent(double startTime) |
344 { | 384 { |
345 ASSERT(m_idleTaskStatus != IdleTaskNotStarted); | 385 ASSERT(m_idleTaskStatus != IdleTaskNotStarted); |
386 double elapsedTime = WTF::monotonicallyIncreasingTime() - startTime; | |
387 if (m_mimeType == MimeTypePng) { | |
388 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobPNGCompleteT imeoutCounter, new CustomCountHistogram("Blink.Canvas.ToBlobTimeoutDuration.PNG. Complete", 0, 10000000, 50)); | |
389 toBlobPNGCompleteTimeoutCounter.count(elapsedTime * 1000000.0); | |
390 } else if (m_mimeType == MimeTypeJpeg) { | |
391 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobJPEGComplete TimeoutCounter, new CustomCountHistogram("Blink.Canvas.ToBlobTimeoutDuration.JPE G.Complete", 0, 10000000, 50)); | |
392 toBlobJPEGCompleteTimeoutCounter.count(elapsedTime * 1000000.0); | |
393 } else { | |
394 DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, toBlobWEBPComplete TimeoutCounter, new CustomCountHistogram("Blink.Canvas.ToBlobTimeoutDuration.WEB P.Complete", 0, 10000000, 50)); | |
395 toBlobWEBPCompleteTimeoutCounter.count(elapsedTime * 1000000.0); | |
396 } | |
346 | 397 |
347 if (m_idleTaskStatus == IdleTaskStarted) { | 398 if (m_idleTaskStatus == IdleTaskStarted) { |
348 // It has taken too long to complete for the idle task. | 399 // It has taken too long to complete for the idle task. |
349 m_idleTaskStatus = IdleTaskSwitchedToMainThreadTask; | 400 m_idleTaskStatus = IdleTaskSwitchedToMainThreadTask; |
350 signalTaskSwitchInCompleteTimeoutEventForTesting(); | 401 signalTaskSwitchInCompleteTimeoutEventForTesting(); |
351 | 402 |
352 if (m_mimeType == MimeTypePng) { | 403 if (m_mimeType == MimeTypePng) { |
404 recordTimeoutSwitch(IdleTaskCompleteTimeout); | |
353 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLIN K_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this)); | 405 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLIN K_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsPngOnMainThread, this)); |
354 } else { | 406 } else { |
355 ASSERT(m_mimeType == MimeTypeJpeg); | 407 ASSERT(m_mimeType == MimeTypeJpeg); |
408 recordTimeoutSwitch(IdleTaskCompleteTimeout); | |
356 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLIN K_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, this)); | 409 Platform::current()->mainThread()->getWebTaskRunner()->postTask(BLIN K_FROM_HERE, bind(&CanvasAsyncBlobCreator::encodeRowsJpegOnMainThread, this)); |
357 } | 410 } |
358 } else { | 411 } else { |
359 ASSERT(m_idleTaskStatus == IdleTaskFailed || m_idleTaskStatus == IdleTas kCompleted); | 412 ASSERT(m_idleTaskStatus == IdleTaskFailed || m_idleTaskStatus == IdleTas kCompleted); |
360 this->signalAlternativeCodePathFinishedForTesting(); | 413 this->signalAlternativeCodePathFinishedForTesting(); |
361 } | 414 } |
362 } | 415 } |
363 | 416 |
364 void CanvasAsyncBlobCreator::postDelayedTaskToMainThread(const WebTraceLocation& location, std::unique_ptr<SameThreadClosure> task, double delayMs) | 417 void CanvasAsyncBlobCreator::postDelayedTaskToMainThread(const WebTraceLocation& location, std::unique_ptr<SameThreadClosure> task, double delayMs) |
365 { | 418 { |
366 DCHECK(isMainThread()); | 419 DCHECK(isMainThread()); |
367 Platform::current()->mainThread()->getWebTaskRunner()->postDelayedTask(locat ion, std::move(task), delayMs); | 420 Platform::current()->mainThread()->getWebTaskRunner()->postDelayedTask(locat ion, std::move(task), delayMs); |
368 } | 421 } |
369 | 422 |
370 } // namespace blink | 423 } // namespace blink |
OLD | NEW |