Clipboard: Skip bitmap decoding when possible

In the most common case, we write an image to the clipboard as a PNG
directly. For this case, skip decoding the PNG data into a bitmap
entirely, since the bitmap is never used.

Bug: 1247356
Change-Id: I8176506d47b0f16712bb6b851ed738ecaa8192bb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3241802
Commit-Queue: Austin Sullivan <[email protected]>
Reviewed-by: Xiyuan Xia <[email protected]>
Cr-Commit-Position: refs/heads/main@{#935516}
diff --git a/chrome/browser/ui/ash/clipboard_util.cc b/chrome/browser/ui/ash/clipboard_util.cc
index 0ce26e1..f894d5ad 100644
--- a/chrome/browser/ui/ash/clipboard_util.cc
+++ b/chrome/browser/ui/ash/clipboard_util.cc
@@ -33,56 +33,30 @@
 void CopyAndMaintainClipboard(
     std::unique_ptr<ui::ClipboardData> data_with_image,
     const std::string& markup_content,
-    scoped_refptr<base::RefCountedString> png_data) {
+    std::vector<uint8_t> png_data) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  std::vector<uint8_t> data(png_data->data().begin(), png_data->data().end());
-
   data_with_image->set_markup_data(markup_content);
-  data_with_image->SetPngData(std::move(data));
+  data_with_image->SetPngData(std::move(png_data));
   ui::ClipboardNonBacked::GetForCurrentThread()->WriteClipboardData(
       std::move(data_with_image));
 }
 
 /*
- * `maintain_clipboard` indicates whether the clipboard state should attempt to
- * be maintained.
  * `clipboard_sequence` is the versioning of the clipboard when we start our
  * copy operation.
- * `callback` alerts whether or not the image was copied to the clipboard while
- * meeting the `maintain_clipboard` state. If the image is copied it will return
- * true, otherwise if the image is not copied because the `clipboard_sequence`
- * does not match, it will return false.
- * `decoded_image` is the image we are attempting to copy to the clipboard.
+ * `callback` alerts whether or not the image was copied to the clipboard. If
+ * the image is copied it will return true, otherwise if the image is not copied
+ * because the `clipboard_sequence` does not match, it will return false.
+ * `png_data` and `html` are different formats of the same image which we are
+ * attempting to copy to the clipboard.
  */
-void CopyImageToClipboard(bool maintain_clipboard,
-                          ui::ClipboardSequenceNumberToken clipboard_sequence,
+void CopyImageToClipboard(ui::ClipboardSequenceNumberToken clipboard_sequence,
                           base::OnceCallback<void(bool)> callback,
-                          scoped_refptr<base::RefCountedString> png_data,
-                          const SkBitmap& decoded_image) {
+                          std::string html,
+                          std::vector<uint8_t> png_data) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Send both HTML and and Image formats to clipboard. HTML format is needed
-  // by ARC, while Image is needed by Hangout.
-  static const char kImageClipboardFormatPrefix[] =
-      "<img src='data:image/png;base64,";
-  static const char kImageClipboardFormatSuffix[] = "'>";
-
-  std::string encoded =
-      base::Base64Encode(base::as_bytes(base::make_span(png_data->data())));
-  std::string html = base::StrCat(
-      {kImageClipboardFormatPrefix, encoded, kImageClipboardFormatSuffix});
-
-  if (!maintain_clipboard ||
-      !ui::ClipboardNonBacked::GetForCurrentThread()->GetClipboardData(
-          nullptr)) {
-    ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
-    clipboard_writer.WriteHTML(base::UTF8ToUTF16(html), std::string());
-    clipboard_writer.WriteImage(decoded_image);
-    std::move(callback).Run(true);
-    return;
-  }
-
   ui::ClipboardSequenceNumberToken current_sequence =
       ui::ClipboardNonBacked::GetForCurrentThread()->GetSequenceNumber(
           ui::ClipboardBuffer::kCopyPaste);
@@ -103,7 +77,28 @@
   if (clipboard_history) {
     clipboard_history->DeleteClipboardItemByClipboardData(current_data.get());
   }
-  CopyAndMaintainClipboard(std::move(current_data), html, png_data);
+  CopyAndMaintainClipboard(std::move(current_data), html, std::move(png_data));
+  std::move(callback).Run(true);
+}
+
+/*
+ * `clipboard_sequence` is the versioning of the clipboard when we start our
+ * copy operation.
+ * `callback` alerts whether or not the image was copied to the clipboard. If
+ * the image is copied it will return true, otherwise if the image is not copied
+ * because the `clipboard_sequence` does not match, it will return false.
+ * `decoded_image` and `html` are different formats of the same image which we
+ * are attempting to copy to the clipboard. */
+void CopyDecodedImageToClipboard(
+    ui::ClipboardSequenceNumberToken clipboard_sequence,
+    base::OnceCallback<void(bool)> callback,
+    std::string html,
+    const SkBitmap& decoded_image) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
+  clipboard_writer.WriteHTML(base::UTF8ToUTF16(html), std::string());
+  clipboard_writer.WriteImage(decoded_image);
   std::move(callback).Run(true);
 }
 
@@ -121,26 +116,49 @@
 
   content::GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE,
-      base::BindOnce(&DecodeImageFileAndCopyToClipboard,
+      base::BindOnce(&MaybeDecodeImageFileAndCopyToClipboard,
                      /*clipboard_sequence=*/ui::ClipboardSequenceNumberToken(),
                      /*maintain_clipboard=*/false, png_data,
                      base::DoNothing()));
 }
 
-void DecodeImageFileAndCopyToClipboard(
+void MaybeDecodeImageFileAndCopyToClipboard(
     ui::ClipboardSequenceNumberToken clipboard_sequence,
     bool maintain_clipboard,
     scoped_refptr<base::RefCountedString> png_data,
     base::OnceCallback<void(bool)> callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Decode the image in sandboxed process because |png_data| comes from
-  // external storage.
-  data_decoder::DecodeImageIsolated(
-      std::vector<uint8_t>(png_data->data().begin(), png_data->data().end()),
-      data_decoder::mojom::ImageCodec::kDefault, false,
-      data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
-      base::BindOnce(&CopyImageToClipboard, maintain_clipboard,
-                     clipboard_sequence, std::move(callback), png_data));
+  // Send both HTML and and Image formats to clipboard. HTML format is needed
+  // by ARC, while Image is needed by Hangout.
+  static const char kImageClipboardFormatPrefix[] =
+      "<img src='data:image/png;base64,";
+  static const char kImageClipboardFormatSuffix[] = "'>";
+
+  std::string encoded =
+      base::Base64Encode(base::as_bytes(base::make_span(png_data->data())));
+  std::string html = base::StrCat(
+      {kImageClipboardFormatPrefix, encoded, kImageClipboardFormatSuffix});
+
+  // Convert string PNG data to more useful vector of bytes.
+  std::vector<uint8_t> png_bytes(png_data->data().begin(),
+                                 png_data->data().end());
+
+  if (!maintain_clipboard ||
+      !ui::ClipboardNonBacked::GetForCurrentThread()->GetClipboardData(
+          nullptr)) {
+    // Decode the image in sandboxed process because |png_data| comes from
+    // external storage.
+    data_decoder::DecodeImageIsolated(
+        std::move(png_bytes), data_decoder::mojom::ImageCodec::kDefault,
+        /*shrink_to_fit=*/false, data_decoder::kDefaultMaxSizeInBytes,
+        gfx::Size(),
+        base::BindOnce(&CopyDecodedImageToClipboard, clipboard_sequence,
+                       std::move(callback), std::move(html)));
+  } else {
+    // Skip image decoding and write the PNG bytes directly to the clipboard.
+    CopyImageToClipboard(clipboard_sequence, std::move(callback),
+                         std::move(html), std::move(png_bytes));
+  }
 }
 }  // namespace clipboard_util