| Index: third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
|
| diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
|
| index fc74f13b75eaf385ef5ef2d985b8cbc77280ab6c..77b78f8722584701cf00152070be25bc7bc9cd4e 100644
|
| --- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
|
| +++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
|
| @@ -46,6 +46,8 @@
|
| #include "core/paint/DeprecatedPaintLayer.h"
|
| #include "platform/MIMETypeRegistry.h"
|
| #include "platform/RuntimeEnabledFeatures.h"
|
| +#include "platform/Task.h"
|
| +#include "platform/ThreadSafeFunctional.h"
|
| #include "platform/graphics/Canvas2DImageBufferSurface.h"
|
| #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
|
| #include "platform/graphics/ImageBuffer.h"
|
| @@ -56,7 +58,6 @@
|
| #include "platform/transforms/AffineTransform.h"
|
| #include "public/platform/Platform.h"
|
| #include "public/platform/WebTraceLocation.h"
|
| -#include "wtf/Functional.h"
|
| #include <math.h>
|
| #include <v8.h>
|
|
|
| @@ -78,6 +79,10 @@ const int MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels
|
| // In Skia, we will also limit width/height to 32767.
|
| const int MaxSkiaDim = 32767; // Maximum width/height in CSS pixels.
|
|
|
| +// A default value of quality argument for toDataURL and toBlob
|
| +// It is in an invalid range (outside 0.0 - 1.0) so that it will not be misinterpreted as a user-input value
|
| +const int UndefinedQualityValue = -1.0;
|
| +
|
| bool canCreateImageBuffer(const IntSize& size)
|
| {
|
| if (size.isEmpty())
|
| @@ -99,6 +104,8 @@ PassRefPtr<Image> createTransparentImage(const IntSize& size)
|
|
|
| } // namespace
|
|
|
| +int HTMLCanvasElement::s_numCanvasInstances = 0;
|
| +
|
| DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver);
|
|
|
| inline HTMLCanvasElement::HTMLCanvasElement(Document& document)
|
| @@ -113,6 +120,7 @@ inline HTMLCanvasElement::HTMLCanvasElement(Document& document)
|
| , m_imageBufferIsClear(false)
|
| {
|
| setHasCustomStyleCallbacks();
|
| + s_numCanvasInstances++;
|
| }
|
|
|
| DEFINE_NODE_FACTORY(HTMLCanvasElement)
|
| @@ -120,7 +128,14 @@ DEFINE_NODE_FACTORY(HTMLCanvasElement)
|
| HTMLCanvasElement::~HTMLCanvasElement()
|
| {
|
| v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory);
|
| -#if !ENABLE(OILPAN)
|
| + s_numCanvasInstances--;
|
| + if (s_numCanvasInstances == 0 && !!getToBlobThreadInstance())
|
| + {
|
| + OwnPtr<WebThread> toBlobThread = getToBlobThreadInstance();
|
| + if (!toBlobThread)
|
| + toBlobThread.clear();
|
| + }
|
| + #if !ENABLE(OILPAN)
|
| for (CanvasObserver* canvasObserver : m_observers)
|
| canvasObserver->canvasDestroyed(this);
|
| // Ensure these go away before the ImageBuffer.
|
| @@ -128,6 +143,15 @@ HTMLCanvasElement::~HTMLCanvasElement()
|
| #endif
|
| }
|
|
|
| +PassOwnPtr<WebThread> HTMLCanvasElement::getToBlobThreadInstance()
|
| +{
|
| + DEFINE_STATIC_LOCAL(OwnPtr<WebThread>, s_toBlobThread, ());
|
| + if (!s_toBlobThread) {
|
| + s_toBlobThread = adoptPtr(Platform::current()->createThread("Async toBlob"));
|
| + }
|
| + return s_toBlobThread.release();
|
| +}
|
| +
|
| void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
|
| {
|
| if (name == widthAttr || name == heightAttr)
|
| @@ -510,7 +534,7 @@ ImageData* HTMLCanvasElement::toImageData(SourceDrawingBuffer sourceBuffer) cons
|
| return imageData;
|
| }
|
|
|
| -String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double* quality, SourceDrawingBuffer sourceBuffer) const
|
| +String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double& quality, SourceDrawingBuffer sourceBuffer) const
|
| {
|
| if (!isPaintable())
|
| return String("data:,");
|
| @@ -529,52 +553,68 @@ String HTMLCanvasElement::toDataURL(const String& mimeType, const ScriptValue& q
|
| exceptionState.throwSecurityError("Tainted canvases may not be exported.");
|
| return String();
|
| }
|
| - double quality;
|
| - double* qualityPtr = nullptr;
|
| + double quality = UndefinedQualityValue;
|
| if (!qualityArgument.isEmpty()) {
|
| v8::Local<v8::Value> v8Value = qualityArgument.v8Value();
|
| if (v8Value->IsNumber()) {
|
| quality = v8Value.As<v8::Number>()->Value();
|
| - qualityPtr = &quality;
|
| }
|
| }
|
| - return toDataURLInternal(mimeType, qualityPtr, BackBuffer);
|
| + return toDataURLInternal(mimeType, quality, BackBuffer);
|
| +}
|
| +
|
| +void HTMLCanvasElement::encodeImageAsync(DOMUint8ClampedArray* imageData, IntSize imageSize, FileCallback* callback, const String& mimeType, double quality)
|
| +{
|
| + OwnPtr<Vector<char>> encodedImage(adoptPtr(new Vector<char>()));
|
| +
|
| + if (!ImageDataBuffer(imageSize, imageData->data()).encodeImage(mimeType, quality, encodedImage.get())) {
|
| + Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bind(&FileCallback::handleEvent, callback, nullptr));
|
| + } else {
|
| + Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, threadSafeBind(&HTMLCanvasElement::createBlobAndCall, AllowCrossThreadAccess(this), encodedImage.release(), mimeType, AllowCrossThreadAccess(callback)));
|
| + }
|
| +}
|
| +
|
| +void HTMLCanvasElement::createBlobAndCall(PassOwnPtr<Vector<char>> encodedImage, const String& mimeType, FileCallback* callback)
|
| +{
|
| + // The main thread takes ownership of encoded image vector
|
| + OwnPtr<Vector<char>> enc(encodedImage);
|
| +
|
| + File* resultBlob = File::create(enc->data(), enc->size(), mimeType);
|
| + Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bind(&FileCallback::handleEvent, callback, resultBlob));
|
| }
|
|
|
| -void HTMLCanvasElement::toBlob(FileCallback* callback, const String& mimeType, const ScriptValue& qualityArgument, ExceptionState& exceptionState) const
|
| +void HTMLCanvasElement::toBlob(FileCallback* callback, const String& mimeType, const ScriptValue& qualityArgument, ExceptionState& exceptionState)
|
| {
|
| if (!originClean()) {
|
| exceptionState.throwSecurityError("Tainted canvases may not be exported.");
|
| return;
|
| }
|
|
|
| - File* resultBlob = nullptr;
|
| if (!isPaintable()) {
|
| // If the canvas element's bitmap has no pixels
|
| + Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bind(&FileCallback::handleEvent, callback, nullptr));
|
| return;
|
| }
|
|
|
| - double quality;
|
| - double* qualityPtr = nullptr;
|
| + double quality = UndefinedQualityValue;
|
| if (!qualityArgument.isEmpty()) {
|
| v8::Local<v8::Value> v8Value = qualityArgument.v8Value();
|
| if (v8Value->IsNumber()) {
|
| quality = v8Value.As<v8::Number>()->Value();
|
| - qualityPtr = &quality;
|
| }
|
| }
|
|
|
| String encodingMimeType = toEncodingMimeType(mimeType);
|
|
|
| ImageData* imageData = toImageData(BackBuffer);
|
| + // imageData unref its data, which we still keep alive for the async toBlob thread
|
| ScopedDisposal<ImageData> disposer(imageData);
|
|
|
| - // Perform image encoding
|
| - Vector<char> encodedImage;
|
| - ImageDataBuffer(imageData->size(), imageData->data()->data()).encodeImage(encodingMimeType, qualityPtr, &encodedImage);
|
| - resultBlob = File::create(encodedImage.data(), encodedImage.size(), encodingMimeType);
|
| + // Add a ref to keep image data alive until completion of encoding
|
| + RefPtr<DOMUint8ClampedArray> imageDataRef(imageData->data());
|
|
|
| - Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bind(&FileCallback::handleEvent, callback, resultBlob));
|
| + // TODO: We cannot guarantee that 'this' will stay alive long enough.
|
| + getToBlobThreadInstance()->taskRunner()->postTask(FROM_HERE, new Task(threadSafeBind(&HTMLCanvasElement::encodeImageAsync, AllowCrossThreadAccess(this), AllowCrossThreadAccess(imageDataRef.release().leakRef()), imageData->size(), AllowCrossThreadAccess(callback), encodingMimeType, quality)));
|
| }
|
|
|
| SecurityOrigin* HTMLCanvasElement::securityOrigin() const
|
|
|