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 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.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/task_type.h"
#include "third_party/blink/renderer/core/dom/document.h"
......@@ -146,6 +148,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
ToBlobFunctionType function_type,
base::TimeTicks start_time,
ExecutionContext* context,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params,
ScriptPromiseResolver* resolver)
: CanvasAsyncBlobCreator(image,
options,
......@@ -153,6 +156,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
nullptr,
start_time,
context,
ukm_params,
resolver) {}
CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
......@@ -162,6 +166,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
V8BlobCallback* callback,
base::TimeTicks start_time,
ExecutionContext* context,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params,
ScriptPromiseResolver* resolver)
: fail_encoder_initialization_for_test_(false),
enforce_idle_encoding_for_test_(false),
......@@ -172,6 +177,7 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(
start_time_(start_time),
static_bitmap_image_loaded_(false),
callback_(callback),
ukm_params_(ukm_params),
script_promise_resolver_(resolver) {
DCHECK(image);
DCHECK(context);
......@@ -468,6 +474,8 @@ void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() {
WrapPersistent(result_blob)));
}
RecordIdentifiabilityMetric();
RecordScaledDurationHistogram(mime_type_,
base::TimeTicks::Now() - start_time_,
image_->width(), image_->height());
......@@ -475,6 +483,23 @@ void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() {
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() {
RecordIdleTaskStatusHistogram(idle_task_status_);
if (function_type_ == kHTMLCanvasToBlobCallback) {
......
......@@ -8,7 +8,9 @@
#include <memory>
#include "base/location.h"
#include "base/optional.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/v8_blob_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_image_encode_options.h"
......@@ -55,11 +57,17 @@ class CORE_EXPORT CanvasAsyncBlobCreator
void ScheduleAsyncBlobCreation(const double& quality);
struct UkmParams {
ukm::UkmRecorder* ukm_recorder;
ukm::SourceId source_id;
};
CanvasAsyncBlobCreator(scoped_refptr<StaticBitmapImage>,
const ImageEncodeOptions* options,
ToBlobFunctionType function_type,
base::TimeTicks start_time,
ExecutionContext*,
base::Optional<UkmParams> ukm_params,
ScriptPromiseResolver*);
CanvasAsyncBlobCreator(scoped_refptr<StaticBitmapImage>,
const ImageEncodeOptions*,
......@@ -67,6 +75,7 @@ class CORE_EXPORT CanvasAsyncBlobCreator
V8BlobCallback*,
base::TimeTicks start_time,
ExecutionContext*,
base::Optional<UkmParams> ukm_params,
ScriptPromiseResolver* = nullptr);
virtual ~CanvasAsyncBlobCreator();
......@@ -132,6 +141,8 @@ class CORE_EXPORT CanvasAsyncBlobCreator
// Used for HTMLCanvasElement only
Member<V8BlobCallback> callback_;
base::Optional<UkmParams> ukm_params_;
// Used for OffscreenCanvas only
Member<ScriptPromiseResolver> script_promise_resolver_;
......@@ -147,6 +158,8 @@ class CORE_EXPORT CanvasAsyncBlobCreator
void IdleTaskStartTimeoutEvent(double quality);
void IdleTaskCompleteTimeoutEvent();
void RecordIdentifiabilityMetric();
};
} // namespace blink
......
......@@ -35,6 +35,7 @@ class MockCanvasAsyncBlobCreator : public CanvasAsyncBlobCreator {
nullptr,
base::TimeTicks(),
document->GetExecutionContext(),
base::make_optional<CanvasAsyncBlobCreator::UkmParams>(),
nullptr) {
if (fail_encoder_initialization)
fail_encoder_initialization_for_test_ = true;
......@@ -129,7 +130,6 @@ class CanvasAsyncBlobCreatorTest : public PageTestBase {
void TearDown() override;
private:
Persistent<MockCanvasAsyncBlobCreator> async_blob_creator_;
};
......@@ -263,7 +263,8 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) {
kDisplayP3ImageColorSpaceName,
kRec2020ImageColorSpaceName};
std::list<String> blob_pixel_formats = {
kRGBA8ImagePixelFormatName, kRGBA16ImagePixelFormatName,
kRGBA8ImagePixelFormatName,
kRGBA16ImagePixelFormatName,
};
// Maximum differences are both observed locally with
......@@ -294,7 +295,9 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) {
source_bitmap_image, options,
CanvasAsyncBlobCreator::ToBlobFunctionType::
kHTMLCanvasConvertToBlobPromise,
base::TimeTicks(), GetFrame().DomWindow(), nullptr);
base::TimeTicks(), GetFrame().DomWindow(),
base::make_optional<CanvasAsyncBlobCreator::UkmParams>(),
nullptr);
ASSERT_TRUE(async_blob_creator->EncodeImageForConvertToBlobTest());
sk_sp<SkData> sk_data = SkData::MakeWithCopy(
......@@ -325,4 +328,4 @@ TEST_F(CanvasAsyncBlobCreatorTest, ColorManagedConvertToBlob) {
}
}
}
}
} // namespace blink
......@@ -22,8 +22,10 @@
namespace blink {
CanvasRenderingContextHost::CanvasRenderingContextHost(HostType host_type)
: host_type_(host_type) {}
CanvasRenderingContextHost::CanvasRenderingContextHost(
HostType host_type,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params)
: host_type_(host_type), ukm_params_(ukm_params) {}
void CanvasRenderingContextHost::RecordCanvasSizeToUMA(const IntSize& size) {
if (did_record_canvas_size_to_uma_)
......@@ -315,7 +317,7 @@ ScriptPromise CanvasRenderingContextHost::convertToBlob(
}
auto* async_creator = MakeGarbageCollected<CanvasAsyncBlobCreator>(
image_bitmap, options, function_type, start_time,
ExecutionContext::From(script_state), resolver);
ExecutionContext::From(script_state), ukm_params_, resolver);
async_creator->ScheduleAsyncBlobCreation(options->quality());
return resolver->Promise();
}
......
......@@ -5,10 +5,13 @@
#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_
#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/core/core_export.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/html/canvas/canvas_async_blob_creator.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/script_state.h"
......@@ -37,7 +40,9 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost,
kCanvasHost,
kOffscreenCanvasHost,
};
CanvasRenderingContextHost(HostType host_type);
CanvasRenderingContextHost(
HostType host_type,
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params);
void RecordCanvasSizeToUMA(const IntSize&);
......@@ -116,6 +121,7 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost,
bool did_fail_to_create_resource_provider_ = false;
bool did_record_canvas_size_to_uma_ = false;
HostType host_type_ = kNone;
base::Optional<CanvasAsyncBlobCreator::UkmParams> ukm_params_;
};
} // namespace blink
......
......@@ -115,7 +115,9 @@ HTMLCanvasElement::HTMLCanvasElement(Document& document)
ExecutionContextLifecycleObserver(GetExecutionContext()),
PageVisibilityObserver(document.GetPage()),
CanvasRenderingContextHost(
CanvasRenderingContextHost::HostType::kCanvasHost),
CanvasRenderingContextHost::HostType::kCanvasHost,
base::make_optional<CanvasAsyncBlobCreator::UkmParams>(
{document.UkmRecorder(), document.UkmSourceID()})),
size_(kDefaultCanvasWidth, kDefaultCanvasHeight),
context_creation_was_blocked_(false),
ignore_reset_(false),
......@@ -376,12 +378,6 @@ ScriptPromise HTMLCanvasElement::convertToBlob(
ScriptState* script_state,
const ImageEncodeOptions* options,
ExceptionState& exception_state) {
RecordIdentifiabilityMetric(
blink::IdentifiableSurface::FromTypeAndInput(
blink::IdentifiableSurface::Type::kCanvasReadback,
context_ ? context_->GetContextType()
: CanvasRenderingContext::kContextTypeUnknown),
0);
return CanvasRenderingContextHost::convertToBlob(script_state, options,
exception_state);
}
......@@ -969,7 +965,8 @@ String HTMLCanvasElement::ToDataURLInternal(
RecordIdentifiabilityMetric(
blink::IdentifiableSurface::FromTypeAndInput(
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;
}
......@@ -1036,16 +1033,11 @@ void HTMLCanvasElement::toBlob(V8BlobCallback* callback,
async_creator = MakeGarbageCollected<CanvasAsyncBlobCreator>(
image_bitmap, options,
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) {
async_creator->ScheduleAsyncBlobCreation(quality);
} else {
......
......@@ -43,7 +43,8 @@ namespace blink {
OffscreenCanvas::OffscreenCanvas(ExecutionContext* context, const IntSize& size)
: CanvasRenderingContextHost(
CanvasRenderingContextHost::HostType::kOffscreenCanvasHost),
CanvasRenderingContextHost::HostType::kOffscreenCanvasHost,
base::make_optional<CanvasAsyncBlobCreator::UkmParams>()),
execution_context_(context),
size_(size) {
// Other code in Blink watches for destruction of the context; be
......
......@@ -60,6 +60,7 @@ class PLATFORM_EXPORT ImageDataBuffer {
const IntSize& size() const { return size_; }
int Height() const { return size_.Height(); }
int Width() const { return size_.Width(); }
size_t ComputeByteSize() const { return pixmap_.computeByteSize(); }
private:
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