910e62b5创建于 1月15日历史提交
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "skia/public/mojom/bitmap_skbitmap_mojom_traits.h"

#include <algorithm>

#include "base/compiler_specific.h"
#include "third_party/skia/include/core/SkPixelRef.h"

namespace mojo {
namespace {

// Maximum reasonable width and height. We don't try to deserialize bitmaps
// bigger than these dimensions.
// These limits are fairly large to accommodate images from the largest possible
// canvas.
constexpr int kMaxWidth = 64 * 1024;
constexpr int kMaxHeight = 64 * 1024;

// A custom SkPixelRef subclass to wrap a BigBuffer storing the pixel data.
class BigBufferPixelRef final : public SkPixelRef {
 public:
  BigBufferPixelRef(mojo_base::BigBuffer buffer,
                    int width,
                    int height,
                    int row_bytes)
      : SkPixelRef(width, height, buffer.data(), row_bytes),
        buffer_(std::move(buffer)) {}
  ~BigBufferPixelRef() override = default;

 private:
  mojo_base::BigBuffer buffer_;
};

bool CreateSkBitmapForPixelData(SkBitmap* b,
                                const SkImageInfo& image_info,
                                base::span<const uint8_t> pixel_data) {
  // Ensure width and height are reasonable.
  if (image_info.width() > kMaxWidth || image_info.height() > kMaxHeight)
    return false;

  // We require incoming bitmaps to be tightly packed by specifying the
  // rowBytes() as minRowBytes(). Then we compare the number of bytes against
  // `pixel_data.size()` later to verify the actual data is tightly packed.
  if (!b->tryAllocPixels(image_info, image_info.minRowBytes()))
    return false;

  // If the image is empty, return success after setting the image info.
  if (image_info.width() == 0 || image_info.height() == 0)
    return true;

  // If these don't match then the number of bytes sent does not match what the
  // rest of the mojom said there should be.
  if (pixel_data.size() != b->computeByteSize())
    return false;

  // Implementation note: This copy is important from a security perspective as
  // it provides the recipient of the SkBitmap with a stable copy of the data.
  // The sender could otherwise continue modifying the shared memory buffer
  // underlying the BigBuffer instance.
  std::ranges::copy(pixel_data, static_cast<uint8_t*>(b->getPixels()));
  b->notifyPixelsChanged();
  return true;
}

}  // namespace

// static
mojo_base::BigBufferView StructTraits<skia::mojom::BitmapN32DataView,
                                      SkBitmap>::pixel_data(const SkBitmap& b) {
  CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
  return mojo_base::BigBufferView(UNSAFE_TODO(
      base::span(static_cast<uint8_t*>(b.getPixels()), b.computeByteSize())));
}

// static
bool StructTraits<skia::mojom::BitmapN32DataView, SkBitmap>::Read(
    skia::mojom::BitmapN32DataView data,
    SkBitmap* b) {
  SkImageInfo image_info;
  if (!data.ReadImageInfo(&image_info))
    return false;

  mojo_base::BigBufferView pixel_data_view;
  if (!data.ReadPixelData(&pixel_data_view))
    return false;

  return CreateSkBitmapForPixelData(b, std::move(image_info),
                                    pixel_data_view.data());
}

// static
mojo_base::BigBufferView
StructTraits<skia::mojom::BitmapWithArbitraryBppDataView, SkBitmap>::pixel_data(
    const SkBitmap& b) {
  CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
  return mojo_base::BigBufferView(UNSAFE_TODO(
      base::span(static_cast<uint8_t*>(b.getPixels()), b.computeByteSize())));
}

// static
bool StructTraits<skia::mojom::BitmapWithArbitraryBppDataView, SkBitmap>::Read(
    skia::mojom::BitmapWithArbitraryBppDataView data,
    SkBitmap* b) {
  SkImageInfo image_info;
  if (!data.ReadImageInfo(&image_info))
    return false;

  mojo_base::BigBufferView pixel_data_view;
  if (!data.ReadPixelData(&pixel_data_view))
    return false;

  return CreateSkBitmapForPixelData(b, std::move(image_info),
                                    pixel_data_view.data());
}

// static
mojo_base::BigBufferView
StructTraits<skia::mojom::BitmapMappedFromTrustedProcessDataView,
             SkBitmap>::pixel_data(const SkBitmap& b) {
  CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
  return mojo_base::BigBufferView(UNSAFE_TODO(
      base::span(static_cast<uint8_t*>(b.getPixels()), b.computeByteSize())));
}

// static
bool StructTraits<
    skia::mojom::BitmapMappedFromTrustedProcessDataView,
    SkBitmap>::Read(skia::mojom::BitmapMappedFromTrustedProcessDataView data,
                    SkBitmap* b) {
  SkImageInfo image_info;
  if (!data.ReadImageInfo(&image_info))
    return false;

  // Ensure width and height are reasonable.
  if (image_info.width() > kMaxWidth || image_info.height() > kMaxHeight)
    return false;

  // If the image is empty, return success after setting the image info.
  if (image_info.width() == 0 || image_info.height() == 0)
    return b->tryAllocPixels(image_info);

  // Otherwise, set a custom PixelRef to retain the BigBuffer. This avoids
  // making another copy of the pixel data.

  mojo_base::BigBufferView pixel_data_view;
  if (!data.ReadPixelData(&pixel_data_view))
    return false;

  // We require incoming bitmaps to be tightly packed by specifying the
  // rowBytes() as minRowBytes(). Then we compare the number of bytes against
  // `pixel_data_view.data().size()` later to verify the actual data is tightly
  // packed.
  if (!b->setInfo(image_info, image_info.minRowBytes()))
    return false;

  // If these don't match then the number of bytes sent does not match what the
  // rest of the mojom said there should be.
  if (b->computeByteSize() != pixel_data_view.data().size())
    return false;

  // Allow the resultant SkBitmap to refer to the given BigBuffer. Note, the
  // sender could continue modifying the pixels of the buffer, which could be a
  // security concern for some applications. The trade-off is performance.
  b->setPixelRef(
      sk_make_sp<BigBufferPixelRef>(
          mojo_base::BigBufferView::ToBigBuffer(std::move(pixel_data_view)),
          image_info.width(), image_info.height(), image_info.minRowBytes()),
      0, 0);
  return true;
}

// static
base::span<const uint8_t>
StructTraits<skia::mojom::InlineBitmapDataView, SkBitmap>::pixel_data(
    const SkBitmap& b) {
  CHECK_EQ(b.rowBytes(), b.info().minRowBytes());
  return UNSAFE_TODO(
      base::span(static_cast<uint8_t*>(b.getPixels()), b.computeByteSize()));
}

// static
bool StructTraits<skia::mojom::InlineBitmapDataView, SkBitmap>::Read(
    skia::mojom::InlineBitmapDataView data,
    SkBitmap* b) {
  SkImageInfo image_info;
  if (!data.ReadImageInfo(&image_info))
    return false;

  mojo::ArrayDataView<uint8_t> pixel_data_view;
  data.GetPixelDataDataView(&pixel_data_view);

  base::span<const uint8_t> UNSAFE_TODO(
      pixel_data_bytes(pixel_data_view.data(), pixel_data_view.size()));

  return CreateSkBitmapForPixelData(b, std::move(image_info),
                                    std::move(pixel_data_bytes));
}

}  // namespace mojo