Commit 15936d15 authored by cfredric's avatar cfredric Committed by Commit Bot

Include image data when reporting UKM from canvas readbacks.

This cl computes digests of the underlying bytes of canvas images when
HTMLCanvasElement.toBlob, HTMLCanvasElement.ToDataURL, or
HTMLCanvasElement.convertToBlob are called.

OffscreenCanvas.convertToBlob is not recorded yet, since we do not yet have a
UKM source or Recorder in that context.

CanvasRenderingContext2D.getImageData and WebGLRenderingContext.readPixels are also not recorded yet.

Bug: 973801
Change-Id: Ic2de567b16c98b613c945afa823a078a4740a7a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2207979
Commit-Queue: Chris Fredrickson <cfredric@google.com>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Reviewed-by: default avatarAsanka Herath <asanka@chromium.org>
Cr-Commit-Position: refs/heads/master@{#772448}
parent 7ba415de
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h"
#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
...@@ -146,6 +148,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator( ...@@ -146,6 +148,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
ToBlobFunctionType function_type, ToBlobFunctionType function_type,
base::TimeTicks start_time, base::TimeTicks start_time,
ExecutionContext* context, ExecutionContext* context,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params,
ScriptPromiseResolver* resolver) ScriptPromiseResolver* resolver)
: CanvasAsyncBlobCreator(image, : CanvasAsyncBlobCreator(image,
options, options,
...@@ -153,6 +156,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator( ...@@ -153,6 +156,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
nullptr, nullptr,
start_time, start_time,
context, context,
ukm_params,
resolver) {} resolver) {}
CanvasAsyncBlobCreator::CanvasAsyncBlobCreator( CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
...@@ -162,6 +166,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator( ...@@ -162,6 +166,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
V8BlobCallback* callback, V8BlobCallback* callback,
base::TimeTicks start_time, base::TimeTicks start_time,
ExecutionContext* context, ExecutionContext* context,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params,
ScriptPromiseResolver* resolver) ScriptPromiseResolver* resolver)
: fail_encoder_initialization_for_test_(false), : fail_encoder_initialization_for_test_(false),
enforce_idle_encoding_for_test_(false), enforce_idle_encoding_for_test_(false),
...@@ -172,6 +177,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator( ...@@ -172,6 +177,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
start_time_(start_time), start_time_(start_time),
static_bitmap_image_loaded_(false), static_bitmap_image_loaded_(false),
callback_(callback), callback_(callback),
ukm_params_(ukm_params),
script_promise_resolver_(resolver) { script_promise_resolver_(resolver) {
DCHECK(image); DCHECK(image);
DCHECK(context); DCHECK(context);
...@@ -468,6 +474,8 @@ void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() { ...@@ -468,6 +474,8 @@ void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() {
WrapPersistent(result_blob))); WrapPersistent(result_blob)));
} }
RecordIdentifiabilityMetric();
RecordScaledDurationHistogram(mime_type_, RecordScaledDurationHistogram(mime_type_,
base::TimeTicks::Now() - start_time_, base::TimeTicks::Now() - start_time_,
image_->width(), image_->height()); image_->width(), image_->height());
...@@ -475,6 +483,23 @@ void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() { ...@@ -475,6 +483,23 @@ void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() {
Dispose(); Dispose();
} }
void CanvasAsyncBlobCreator::RecordIdentifiabilityMetric() {
if (!ukm_params_.has_value())
return;
// Creating this ImageDataBuffer has some overhead, namely getting the SkImage
// and computing the pixmap.
std::unique_ptr<ImageDataBuffer> data_buffer =
ImageDataBuffer::Create(image_);
if (!data_buffer)
return;
blink::IdentifiabilityMetricBuilder(ukm_params_->source_id)
.Set(blink::IdentifiableSurface::FromTypeAndInput(
blink::IdentifiableSurface::Type::kCanvasReadback, 0),
blink::IdentifiabilityDigestOfBytes(base::make_span(
data_buffer->Pixels(), data_buffer->ComputeByteSize())))
.Record(ukm_params_->ukm_recorder);
}
void CanvasAsyncBlobCreator::CreateNullAndReturnResult() { void CanvasAsyncBlobCreator::CreateNullAndReturnResult() {
RecordIdleTaskStatusHistogram(idle_task_status_); RecordIdleTaskStatusHistogram(idle_task_status_);
if (function_type_ == kHTMLCanvasToBlobCallback) { if (function_type_ == kHTMLCanvasToBlobCallback) {
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
#include <memory> #include <memory>
#include "base/location.h" #include "base/location.h"
#include "base/optional.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_blob_callback.h" #include "third_party/blink/renderer/bindings/core/v8/v8_blob_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_image_encode_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_image_encode_options.h"
...@@ -55,11 +57,17 @@ class CORE_EXPORT CanvasAsyncBlobCreator ...@@ -55,11 +57,17 @@ class CORE_EXPORT CanvasAsyncBlobCreator
void ScheduleAsyncBlobCreation(const double& quality); void ScheduleAsyncBlobCreation(const double& quality);
struct UkmParams {
ukm::UkmRecorder* ukm_recorder;
ukm::SourceId source_id;
};
CanvasAsyncBlobCreator(scoped_refptr<StaticBitmapImage>, CanvasAsyncBlobCreator(scoped_refptr<StaticBitmapImage>,
const ImageEncodeOptions* options, const ImageEncodeOptions* options,
ToBlobFunctionType function_type, ToBlobFunctionType function_type,
base::TimeTicks start_time, base::TimeTicks start_time,
ExecutionContext*, ExecutionContext*,
base::Optional<UkmParams> ukm_params,
ScriptPromiseResolver*); ScriptPromiseResolver*);
CanvasAsyncBlobCreator(scoped_refptr<StaticBitmapImage>, CanvasAsyncBlobCreator(scoped_refptr<StaticBitmapImage>,
const ImageEncodeOptions*, const ImageEncodeOptions*,
...@@ -67,6 +75,7 @@ class CORE_EXPORT CanvasAsyncBlobCreator ...@@ -67,6 +75,7 @@ class CORE_EXPORT CanvasAsyncBlobCreator
V8BlobCallback*, V8BlobCallback*,
base::TimeTicks start_time, base::TimeTicks start_time,
ExecutionContext*, ExecutionContext*,
base::Optional<UkmParams> ukm_params,
ScriptPromiseResolver* = nullptr); ScriptPromiseResolver* = nullptr);
virtual ~CanvasAsyncBlobCreator(); virtual ~CanvasAsyncBlobCreator();
...@@ -132,6 +141,8 @@ class CORE_EXPORT CanvasAsyncBlobCreator ...@@ -132,6 +141,8 @@ class CORE_EXPORT CanvasAsyncBlobCreator
// Used for HTMLCanvasElement only // Used for HTMLCanvasElement only
Member<V8BlobCallback> callback_; Member<V8BlobCallback> callback_;
base::Optional<UkmParams> ukm_params_;
// Used for OffscreenCanvas only // Used for OffscreenCanvas only
Member<ScriptPromiseResolver> script_promise_resolver_; Member<ScriptPromiseResolver> script_promise_resolver_;
...@@ -147,6 +158,8 @@ class CORE_EXPORT CanvasAsyncBlobCreator ...@@ -147,6 +158,8 @@ class CORE_EXPORT CanvasAsyncBlobCreator
void IdleTaskStartTimeoutEvent(double quality); void IdleTaskStartTimeoutEvent(double quality);
void IdleTaskCompleteTimeoutEvent(); void IdleTaskCompleteTimeoutEvent();
void RecordIdentifiabilityMetric();
}; };
} // namespace blink } // namespace blink
......
...@@ -35,6 +35,7 @@ class MockCanvasAsyncBlobCreator : public CanvasAsyncBlobCreator { ...@@ -35,6 +35,7 @@ class MockCanvasAsyncBlobCreator : public CanvasAsyncBlobCreator {
nullptr, nullptr,
base::TimeTicks(), base::TimeTicks(),
document->GetExecutionContext(), document->GetExecutionContext(),
base::make_optional<CanvasAsyncBlobCreator::UkmParams>(),
nullptr) { nullptr) {
if (fail_encoder_initialization) if (fail_encoder_initialization)
fail_encoder_initialization_for_test_ = true; fail_encoder_initialization_for_test_ = true;
...@@ -129,7 +130,6 @@ class CanvasAsyncBlobCreatorTest : public PageTestBase { ...@@ -129,7 +130,6 @@ class CanvasAsyncBlobCreatorTest : public PageTestBase {
void TearDown() override; void TearDown() override;
private: private:
Persistent<MockCanvasAsyncBlobCreator> async_blob_creator_; Persistent<MockCanvasAsyncBlobCreator> async_blob_creator_;
}; };
...@@ -263,7 +263,8 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) { ...@@ -263,7 +263,8 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) {
kDisplayP3ImageColorSpaceName, kDisplayP3ImageColorSpaceName,
kRec2020ImageColorSpaceName}; kRec2020ImageColorSpaceName};
std::list<String> blob_pixel_formats = { std::list<String> blob_pixel_formats = {
kRGBA8ImagePixelFormatName, kRGBA16ImagePixelFormatName, kRGBA8ImagePixelFormatName,
kRGBA16ImagePixelFormatName,
}; };
// Maximum differences are both observed locally with // Maximum differences are both observed locally with
...@@ -294,7 +295,9 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) { ...@@ -294,7 +295,9 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) {
source_bitmap_image, options, source_bitmap_image, options,
CanvasAsyncBlobCreator::ToBlobFunctionType:: CanvasAsyncBlobCreator::ToBlobFunctionType::
kHTMLCanvasConvertToBlobPromise, kHTMLCanvasConvertToBlobPromise,
base::TimeTicks(), GetFrame().DomWindow(), nullptr); base::TimeTicks(), GetFrame().DomWindow(),
base::make_optional<CanvasAsyncBlobCreator::UkmParams>(),
nullptr);
ASSERT_TRUE(async_blob_creator->EncodeImageForConvertToBlobTest()); ASSERT_TRUE(async_blob_creator->EncodeImageForConvertToBlobTest());
sk_sp<SkData> sk_data = SkData::MakeWithCopy( sk_sp<SkData> sk_data = SkData::MakeWithCopy(
...@@ -325,4 +328,4 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) { ...@@ -325,4 +328,4 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) {
} }
} }
} }
} } // namespace blink
...@@ -22,8 +22,10 @@ ...@@ -22,8 +22,10 @@
namespace blink { namespace blink {
CanvasRenderingContextHost::CanvasRenderingContextHost(HostType host_type) CanvasRenderingContextHost::CanvasRenderingContextHost(
: host_type_(host_type) {} HostType host_type,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params)
: host_type_(host_type), ukm_params_(ukm_params) {}
void CanvasRenderingContextHost::RecordCanvasSizeToUMA(const IntSize& size) { void CanvasRenderingContextHost::RecordCanvasSizeToUMA(const IntSize& size) {
if (did_record_canvas_size_to_uma_) if (did_record_canvas_size_to_uma_)
...@@ -315,7 +317,7 @@ ScriptPromise CanvasRenderingContextHost::convertToBlob( ...@@ -315,7 +317,7 @@ ScriptPromise CanvasRenderingContextHost::convertToBlob(
} }
auto* async_creator = MakeGarbageCollected<CanvasAsyncBlobCreator>( auto* async_creator = MakeGarbageCollected<CanvasAsyncBlobCreator>(
image_bitmap, options, function_type, start_time, image_bitmap, options, function_type, start_time,
ExecutionContext::From(script_state), resolver); ExecutionContext::From(script_state), ukm_params_, resolver);
async_creator->ScheduleAsyncBlobCreation(options->quality()); async_creator->ScheduleAsyncBlobCreation(options->quality());
return resolver->Promise(); return resolver->Promise();
} }
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CANVAS_CANVAS_RENDERING_CONTEXT_HOST_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CANVAS_CANVAS_RENDERING_CONTEXT_HOST_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CANVAS_CANVAS_RENDERING_CONTEXT_HOST_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CANVAS_CANVAS_RENDERING_CONTEXT_HOST_H_
#include "base/optional.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/events/event_dispatcher.h" #include "third_party/blink/renderer/core/dom/events/event_dispatcher.h"
#include "third_party/blink/renderer/core/dom/events/event_target.h" #include "third_party/blink/renderer/core/dom/events/event_target.h"
#include "third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h"
#include "third_party/blink/renderer/core/html/canvas/canvas_image_source.h" #include "third_party/blink/renderer/core/html/canvas/canvas_image_source.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h"
...@@ -37,7 +40,9 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost, ...@@ -37,7 +40,9 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost,
kCanvasHost, kCanvasHost,
kOffscreenCanvasHost, kOffscreenCanvasHost,
}; };
CanvasRenderingContextHost(HostType host_type); CanvasRenderingContextHost(
HostType host_type,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params);
void RecordCanvasSizeToUMA(const IntSize&); void RecordCanvasSizeToUMA(const IntSize&);
...@@ -116,6 +121,7 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost, ...@@ -116,6 +121,7 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost,
bool did_fail_to_create_resource_provider_ = false; bool did_fail_to_create_resource_provider_ = false;
bool did_record_canvas_size_to_uma_ = false; bool did_record_canvas_size_to_uma_ = false;
HostType host_type_ = kNone; HostType host_type_ = kNone;
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params_;
}; };
} // namespace blink } // namespace blink
......
...@@ -115,7 +115,9 @@ HTMLCanvasElement::HTMLCanvasElement(Document& document) ...@@ -115,7 +115,9 @@ HTMLCanvasElement::HTMLCanvasElement(Document& document)
ExecutionContextLifecycleObserver(GetExecutionContext()), ExecutionContextLifecycleObserver(GetExecutionContext()),
PageVisibilityObserver(document.GetPage()), PageVisibilityObserver(document.GetPage()),
CanvasRenderingContextHost( CanvasRenderingContextHost(
CanvasRenderingContextHost::HostType::kCanvasHost), CanvasRenderingContextHost::HostType::kCanvasHost,
base::make_optional<CanvasAsyncBlobCreator::UkmParams>(
{document.UkmRecorder(), document.UkmSourceID()})),
size_(kDefaultCanvasWidth, kDefaultCanvasHeight), size_(kDefaultCanvasWidth, kDefaultCanvasHeight),
context_creation_was_blocked_(false), context_creation_was_blocked_(false),
ignore_reset_(false), ignore_reset_(false),
...@@ -376,12 +378,6 @@ ScriptPromise HTMLCanvasElement::convertToBlob( ...@@ -376,12 +378,6 @@ ScriptPromise HTMLCanvasElement::convertToBlob(
ScriptState* script_state, ScriptState* script_state,
const ImageEncodeOptions* options, const ImageEncodeOptions* options,
ExceptionState& exception_state) { ExceptionState& exception_state) {
RecordIdentifiabilityMetric(
blink::IdentifiableSurface::FromTypeAndInput(
blink::IdentifiableSurface::Type::kCanvasReadback,
context_ ? context_->GetContextType()
: CanvasRenderingContext::kContextTypeUnknown),
0);
return CanvasRenderingContextHost::convertToBlob(script_state, options, return CanvasRenderingContextHost::convertToBlob(script_state, options,
exception_state); exception_state);
} }
...@@ -969,7 +965,8 @@ String HTMLCanvasElement::ToDataURLInternal( ...@@ -969,7 +965,8 @@ String HTMLCanvasElement::ToDataURLInternal(
RecordIdentifiabilityMetric( RecordIdentifiabilityMetric(
blink::IdentifiableSurface::FromTypeAndInput( blink::IdentifiableSurface::FromTypeAndInput(
blink::IdentifiableSurface::Type::kCanvasReadback, final_digest), blink::IdentifiableSurface::Type::kCanvasReadback, final_digest),
blink::IdentifiabilityDigestOfBytes(data_url.Span8())); blink::IdentifiabilityDigestOfBytes(base::make_span(
data_buffer->Pixels(), data_buffer->ComputeByteSize())));
return data_url; return data_url;
} }
...@@ -1036,16 +1033,11 @@ void HTMLCanvasElement::toBlob(V8BlobCallback* callback, ...@@ -1036,16 +1033,11 @@ void HTMLCanvasElement::toBlob(V8BlobCallback* callback,
async_creator = MakeGarbageCollected<CanvasAsyncBlobCreator>( async_creator = MakeGarbageCollected<CanvasAsyncBlobCreator>(
image_bitmap, options, image_bitmap, options,
CanvasAsyncBlobCreator::kHTMLCanvasToBlobCallback, callback, start_time, CanvasAsyncBlobCreator::kHTMLCanvasToBlobCallback, callback, start_time,
GetExecutionContext()); GetExecutionContext(),
base::make_optional<CanvasAsyncBlobCreator::UkmParams>(
{GetDocument().UkmRecorder(), GetDocument().UkmSourceID()}));
} }
RecordIdentifiabilityMetric(
blink::IdentifiableSurface::FromTypeAndInput(
blink::IdentifiableSurface::Type::kCanvasReadback,
context_ ? context_->GetContextType()
: CanvasRenderingContext::kContextTypeUnknown),
0);
if (async_creator) { if (async_creator) {
async_creator->ScheduleAsyncBlobCreation(quality); async_creator->ScheduleAsyncBlobCreation(quality);
} else { } else {
......
...@@ -43,7 +43,8 @@ namespace blink { ...@@ -43,7 +43,8 @@ namespace blink {
OffscreenCanvas::OffscreenCanvas(ExecutionContext* context, const IntSize& size) OffscreenCanvas::OffscreenCanvas(ExecutionContext* context, const IntSize& size)
: CanvasRenderingContextHost( : CanvasRenderingContextHost(
CanvasRenderingContextHost::HostType::kOffscreenCanvasHost), CanvasRenderingContextHost::HostType::kOffscreenCanvasHost,
base::make_optional<CanvasAsyncBlobCreator::UkmParams>()),
execution_context_(context), execution_context_(context),
size_(size) { size_(size) {
// Other code in Blink watches for destruction of the context; be // Other code in Blink watches for destruction of the context; be
......
...@@ -60,6 +60,7 @@ class PLATFORM_EXPORT ImageDataBuffer { ...@@ -60,6 +60,7 @@ class PLATFORM_EXPORT ImageDataBuffer {
const IntSize& size() const { return size_; } const IntSize& size() const { return size_; }
int Height() const { return size_.Height(); } int Height() const { return size_.Height(); }
int Width() const { return size_.Width(); } int Width() const { return size_.Width(); }
size_t ComputeByteSize() const { return pixmap_.computeByteSize(); }
private: private:
ImageDataBuffer(const IntSize&, ImageDataBuffer(const IntSize&,
......
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