From: csagan5 <32685696+csagan5@users.noreply.github.com>
Date: Mon, 30 Apr 2018 15:39:58 +0200
Subject: Started work on canvas randomisation

Add shuffling for TextMetrics
---
 .../renderer/core/html/canvas/canvas_async_blob_creator.cc | 14 +++++++-------
 .../renderer/core/html/canvas/canvas_async_blob_creator.h  |  6 +++---
 .../blink/renderer/core/html/canvas/html_canvas_element.cc |  4 ++--
 .../blink/renderer/core/html/canvas/text_metrics.cc        |  4 ++++
 third_party/blink/renderer/core/html/canvas/text_metrics.h |  2 ++
 .../blink/renderer/core/inspector/InspectorAuditsAgent.cpp |  2 +-
 .../renderer/core/offscreencanvas/offscreen_canvas.cc      |  6 +++++-
 .../renderer/modules/accessibility/ax_layout_object.cc     |  2 +-
 .../modules/canvas/canvas2d/base_rendering_context_2d.cc   |  3 +++
 .../modules/canvas/canvas2d/canvas_rendering_context_2d.cc |  6 +++++-
 .../blink/renderer/platform/graphics/image_data_buffer.cc  |  6 ++++--
 .../blink/renderer/platform/graphics/image_data_buffer.h   |  3 ++-
 12 files changed, 39 insertions(+), 19 deletions(-)

diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.cc
@@ -218,14 +218,14 @@ void CanvasAsyncBlobCreator::Dispose() {
   image_ = nullptr;
 }
 
-bool CanvasAsyncBlobCreator::EncodeImage(const double& quality) {
+bool CanvasAsyncBlobCreator::EncodeImage(const double& quality, const double shuffleAmt) {
   std::unique_ptr<ImageDataBuffer> buffer = ImageDataBuffer::Create(src_data_);
   if (!buffer)
     return false;
-  return buffer->EncodeImage("image/webp", quality, &encoded_image_);
+  return buffer->EncodeImage("image/webp", quality, shuffleAmt, &encoded_image_);
 }
 
-void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) {
+void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality, const double shuffleAmt) {
   if (!static_bitmap_image_loaded_) {
     context_->GetTaskRunner(TaskType::kCanvasBlobSerialization)
         ->PostTask(FROM_HERE,
@@ -239,7 +239,7 @@ void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) {
       // When OffscreenCanvas.convertToBlob() occurs on worker thread,
       // we do not need to use background task runner to reduce load on main.
       // So we just directly encode images on the worker thread.
-      if (!EncodeImage(quality)) {
+      if (!EncodeImage(quality, shuffleAmt)) {
         context_->GetTaskRunner(TaskType::kCanvasBlobSerialization)
             ->PostTask(
                 FROM_HERE,
@@ -258,7 +258,7 @@ void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) {
       BackgroundTaskRunner::PostOnBackgroundThread(
           FROM_HERE,
           CrossThreadBind(&CanvasAsyncBlobCreator::EncodeImageOnEncoderThread,
-                          WrapCrossThreadPersistent(this), quality));
+                          WrapCrossThreadPersistent(this), quality, shuffleAmt));
     }
   } else {
     idle_task_status_ = kIdleTaskNotStarted;
@@ -430,11 +430,11 @@ void CanvasAsyncBlobCreator::CreateNullAndReturnResult() {
   Dispose();
 }
 
-void CanvasAsyncBlobCreator::EncodeImageOnEncoderThread(double quality) {
+void CanvasAsyncBlobCreator::EncodeImageOnEncoderThread(double quality, double shuffleAmt) {
   DCHECK(!IsMainThread());
   DCHECK(mime_type_ == kMimeTypeWebp);
 
-  if (!EncodeImage(quality)) {
+  if (!EncodeImage(quality, shuffleAmt)) {
     PostCrossThreadTask(
         *parent_frame_task_runner_, FROM_HERE,
         CrossThreadBind(&CanvasAsyncBlobCreator::CreateNullAndReturnResult,
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h
@@ -38,7 +38,7 @@ class CORE_EXPORT CanvasAsyncBlobCreator
                                         ExecutionContext*,
                                         ScriptPromiseResolver*);
 
-  void ScheduleAsyncBlobCreation(const double& quality);
+  void ScheduleAsyncBlobCreation(const double& quality, const double shuffleAmt);
   virtual ~CanvasAsyncBlobCreator();
   enum MimeType {
     kMimeTypePng,
@@ -130,7 +130,7 @@ class CORE_EXPORT CanvasAsyncBlobCreator
   Member<ScriptPromiseResolver> script_promise_resolver_;
 
   void LoadStaticBitmapImage();
-  bool EncodeImage(const double&);
+  bool EncodeImage(const double&, const double);
 
   // PNG, JPEG
   bool InitializeEncoder(double quality);
@@ -138,7 +138,7 @@ class CORE_EXPORT CanvasAsyncBlobCreator
                                           // without deadline
 
   // WEBP
-  void EncodeImageOnEncoderThread(double quality);
+  void EncodeImageOnEncoderThread(double quality, double shuffleAmt);
 
   void IdleTaskStartTimeoutEvent(double quality);
   void IdleTaskCompleteTimeoutEvent();
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -850,7 +850,7 @@ String HTMLCanvasElement::ToDataURLInternal(
     std::unique_ptr<ImageDataBuffer> data_buffer =
         ImageDataBuffer::Create(image_bitmap);
     if (data_buffer)
-      return data_buffer->ToDataURL(encoding_mime_type, quality);
+      return data_buffer->ToDataURL(encoding_mime_type, quality, GetDocument().GetShuffleFactorX());
   }
   return String("data:,");
 }
@@ -916,7 +916,7 @@ void HTMLCanvasElement::toBlob(V8BlobCallback* callback,
   }
 
   if (async_creator) {
-    async_creator->ScheduleAsyncBlobCreation(quality);
+    async_creator->ScheduleAsyncBlobCreation(quality, GetDocument().GetShuffleFactorX());
   } else {
     GetDocument()
         .GetTaskRunner(TaskType::kCanvasBlobSerialization)
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.cc b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.cc
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
@@ -47,6 +47,10 @@ float TextMetrics::GetFontBaseline(const TextBaseline& text_baseline,
   return 0;
 }
 
+void TextMetrics::Shuffle(const double amt) {
+  //TODO: shuffle here
+}
+
 void TextMetrics::Update(const Font& font,
                          const TextDirection& direction,
                          const TextBaseline& baseline,
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.h b/third_party/blink/renderer/core/html/canvas/text_metrics.h
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.h
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.h
@@ -67,6 +67,8 @@ class CORE_EXPORT TextMetrics final : public ScriptWrappable {
 
   static float GetFontBaseline(const TextBaseline&, const FontMetrics&);
 
+  void Shuffle(const double amt);
+
  private:
   void Update(const Font&,
               const TextDirection&,
diff --git a/third_party/blink/renderer/core/inspector/InspectorAuditsAgent.cpp b/third_party/blink/renderer/core/inspector/InspectorAuditsAgent.cpp
--- a/third_party/blink/renderer/core/inspector/InspectorAuditsAgent.cpp
+++ b/third_party/blink/renderer/core/inspector/InspectorAuditsAgent.cpp
@@ -56,7 +56,7 @@ bool EncodeAsImage(char* body,
 
   String mime_type = "image/";
   mime_type.append(encoding);
-  return image_to_encode->EncodeImage(mime_type, quality, output);
+  return image_to_encode->EncodeImage(mime_type, quality, 0, output);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h"
 
 #include <memory>
+#include "base/rand_util.h"
 #include <utility>
 
 #include "gpu/config/gpu_feature_info.h"
@@ -403,7 +404,10 @@ ScriptPromise OffscreenCanvas::convertToBlob(ScriptState* script_state,
     CanvasAsyncBlobCreator* async_creator = CanvasAsyncBlobCreator::Create(
         snapshot, encoding_mime_type, start_time,
         ExecutionContext::From(script_state), resolver);
-    async_creator->ScheduleAsyncBlobCreation(options.quality());
+
+    // add +/- 3% noise against fingerprinting
+    double shuffleAmt = (base::RandDouble() - 0.5) * 0.03;
+    async_creator->ScheduleAsyncBlobCreation(options.quality(), shuffleAmt);
     return resolver->Promise();
   } else {
     exception_state.ThrowDOMException(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -1009,7 +1009,7 @@ String AXLayoutObject::ImageDataUrl(const IntSize& max_size) const {
   if (!buffer)
     return String();
 
-  return buffer->ToDataURL("image/png", 1.0);
+  return buffer->ToDataURL("image/png", 1.0, document->GetShuffleFactorX());
 }
 
 String AXLayoutObject::GetText() const {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -1620,6 +1620,9 @@ ImageData* BaseRenderingContext2D::getImageData(
   const CanvasColorParams& color_params = ColorParams();
   scoped_refptr<StaticBitmapImage> snapshot = GetImage(kPreferNoAcceleration);
 
+  //TODO: calculate some random value and use it to shuffle pixel data in 'snapshot'
+  // it should StaticBitmapImage somehow
+
   if (!StaticBitmapImage::ConvertToArrayBufferContents(
           snapshot, contents, image_data_rect, color_params, IsAccelerated())) {
     exception_state.ThrowRangeError("Out of memory at ImageData creation");
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -773,8 +773,12 @@ TextMetrics* CanvasRenderingContext2D::measureText(const String& text) {
   else
     direction = ToTextDirection(GetState().GetDirection(), canvas());
 
-  return TextMetrics::Create(font, direction, GetState().GetTextBaseline(),
+  TextMetrics* textMetrics = TextMetrics::Create(font, direction, GetState().GetTextBaseline(),
                              GetState().GetTextAlign(), text);
+
+  textMetrics->Shuffle(canvas()->GetDocument().GetShuffleFactorX());
+
+  return textMetrics;
 }
 
 void CanvasRenderingContext2D::DrawTextInternal(
diff --git a/third_party/blink/renderer/platform/graphics/image_data_buffer.cc b/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
--- a/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
@@ -118,7 +118,9 @@ const unsigned char* ImageDataBuffer::Pixels() const {
 
 bool ImageDataBuffer::EncodeImage(const String& mime_type,
                                   const double& quality,
+                                  const double shuffleAmt,
                                   Vector<unsigned char>* encoded_image) const {
+  //TODO: this is the only place where shuffleAmt should be used
   DCHECK(is_valid_);
 
   if (mime_type == "image/jpeg") {
@@ -154,12 +156,12 @@ bool ImageDataBuffer::EncodeImage(const String& mime_type,
 }
 
 String ImageDataBuffer::ToDataURL(const String& mime_type,
-                                  const double& quality) const {
+                                  const double& quality, const double shuffleAmt) const {
   DCHECK(is_valid_);
   DCHECK(MIMETypeRegistry::IsSupportedImageMIMETypeForEncoding(mime_type));
 
   Vector<unsigned char> result;
-  if (!EncodeImage(mime_type, quality, &result))
+  if (!EncodeImage(mime_type, quality, shuffleAmt, &result))
     return "data:,";
 
   return "data:" + mime_type + ";base64," + Base64Encode(result);
diff --git a/third_party/blink/renderer/platform/graphics/image_data_buffer.h b/third_party/blink/renderer/platform/graphics/image_data_buffer.h
--- a/third_party/blink/renderer/platform/graphics/image_data_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/image_data_buffer.h
@@ -47,9 +47,10 @@ class PLATFORM_EXPORT ImageDataBuffer {
       scoped_refptr<StaticBitmapImage>);
   static std::unique_ptr<ImageDataBuffer> Create(const SkPixmap&);
 
-  String ToDataURL(const String& mime_type, const double& quality) const;
+  String ToDataURL(const String& mime_type, const double& quality, const double shuffleAmt) const;
   bool EncodeImage(const String& mime_type,
                    const double& quality,
+                   double shuffleAmt,
                    Vector<unsigned char>* encoded_image) const;
   const unsigned char* Pixels() const;
   const IntSize& size() const { return size_; }
-- 
2.7.4

