blob: 20646d41aa297dc593e09b77a22bc13860986e85 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
#define CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
#include <memory>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "ui/gfx/image/image_skia.h"
namespace base {
class TimeTicks;
} // namespace base
// Stores compressed thumbnail data for a tab and can vend that data as an
// uncompressed image to observers.
class ThumbnailImage : public base::RefCounted<ThumbnailImage> {
public:
// Smart pointer to reference-counted compressed image data; in this case
// JPEG format.
using CompressedThumbnailData =
scoped_refptr<base::RefCountedData<std::vector<uint8_t>>>;
class Subscription {
public:
Subscription() = delete;
~Subscription();
using UncompressedImageCallback =
base::RepeatingCallback<void(gfx::ImageSkia)>;
using CompressedImageCallback =
base::RepeatingCallback<void(CompressedThumbnailData)>;
// Set callbacks to receive image data. Subscribers are not allowed
// to unsubscribe (by destroying |this|) from the callback. If
// necessary, post a task to destroy it soon after.
void SetUncompressedImageCallback(UncompressedImageCallback callback) {
uncompressed_image_callback_ = std::move(callback);
}
void SetCompressedImageCallback(CompressedImageCallback callback) {
compressed_image_callback_ = std::move(callback);
}
// Provides a desired aspect ratio and minimum size that the observer will
// accept. If not specified, or if available thumbnail data is smaller in
// either dimension than this value, OnThumbnailImageAvailable will be
// called with an uncropped image. If this value is specified, and the
// available image is larger, the image passed to OnThumbnailImageAvailable
// will be cropped to the same aspect ratio (but otherwise unchanged,
// including scale).
//
// OnCompressedThumbnailDataAvailable is not affected by this value.
//
// This method is used to ensure that except for very small thumbnails, the
// image passed to OnThumbnailImageAvailable fits the needs of the observer
// for display purposes, without the observer having to further crop the
// image. The default is unspecified.
void SetSizeHint(const base::Optional<gfx::Size>& size_hint) {
size_hint_ = size_hint;
}
private:
friend class ThumbnailImage;
explicit Subscription(scoped_refptr<ThumbnailImage> thumbnail);
scoped_refptr<ThumbnailImage> thumbnail_;
base::Optional<gfx::Size> size_hint_;
UncompressedImageCallback uncompressed_image_callback_;
CompressedImageCallback compressed_image_callback_;
};
// Represents the endpoint
class Delegate {
public:
// Called whenever the thumbnail starts or stops being observed.
// Because updating the thumbnail could be an expensive operation, it's
// useful to track when there are no observers. Default behavior is no-op.
virtual void ThumbnailImageBeingObservedChanged(bool is_being_observed) = 0;
protected:
virtual ~Delegate();
private:
friend class ThumbnailImage;
ThumbnailImage* thumbnail_ = nullptr;
};
explicit ThumbnailImage(Delegate* delegate);
bool has_data() const { return data_.get(); }
// Subscribe to thumbnail updates. See |Subscription| to set a
// callback and conigure additional options.
//
// Even if a callback is not set, the subscription influences
// thumbnail capture. It should be destroyed when updates are not
// needed. It is designed to be stored in base::Optional, created and
// destroyed as needed.
std::unique_ptr<Subscription> Subscribe();
// Sets the SkBitmap data and notifies observers with the resulting image.
void AssignSkBitmap(SkBitmap bitmap);
// Clears the currently set |data_|, for when the current thumbnail is no
// longer valid to display.
void ClearData();
// Requests that a thumbnail image be made available to observers. Does not
// guarantee that Observer::OnThumbnailImageAvailable() will be called, or how
// long it will take, though in most cases it should happen very quickly.
void RequestThumbnailImage();
// Similar to RequestThumbnailImage() but requests only the compressed JPEG
// data. Users should listen for a call to
// Observer::OnCompressedThumbnailDataAvailable().
void RequestCompressedThumbnailData();
// Returns the size of the compressed data backing this thumbnail.
// This size can be 0. Additionally, since this data is refcounted,
// it's possible this returns 0 even if the data is still allocated. A
// client can hold a reference to it after |this| drops its reference.
size_t GetCompressedDataSizeInBytes() const;
void set_async_operation_finished_callback_for_testing(
base::RepeatingClosure callback) {
async_operation_finished_callback_ = std::move(callback);
}
private:
friend class Delegate;
friend class ThumbnailImageTest;
friend class base::RefCounted<ThumbnailImage>;
virtual ~ThumbnailImage();
void AssignJPEGData(base::TimeTicks assign_sk_bitmap_time,
std::vector<uint8_t> data);
bool ConvertJPEGDataToImageSkiaAndNotifyObservers();
void NotifyUncompressedDataObservers(gfx::ImageSkia image);
void NotifyCompressedDataObservers(CompressedThumbnailData data);
static std::vector<uint8_t> CompressBitmap(SkBitmap bitmap);
static gfx::ImageSkia UncompressImage(CompressedThumbnailData compressed);
// Crops and returns a preview from a thumbnail of an entire web page. Uses
// logic appropriate for fixed-aspect previews (e.g. hover cards).
static gfx::ImageSkia CropPreviewImage(const gfx::ImageSkia& source_image,
const gfx::Size& minimum_size);
void HandleSubscriptionDestroyed(Subscription* subscription);
Delegate* delegate_;
// This is a scoped_refptr to immutable data. Once set, the wrapped
// data must not be modified; it is referenced by other threads.
// |data_| itself can be changed as this does not affect references to
// the old data.
CompressedThumbnailData data_;
// Subscriptions are inserted on |Subscribe()| calls and removed when
// they are destroyed via callback. The order of subscriber
// notification doesn't matter, so don't maintain any ordering. Since
// the number of subscribers for a given thumbnail is expected to be
// small, doing a linear search to remove a subscriber is fine.
std::vector<Subscription*> subscribers_;
// Called when an asynchronous operation (such as encoding image data upon
// assignment or decoding image data for observers) finishes or fails.
// Intended for unit tests that want to wait for internal operations following
// AssignSkBitmap() or RequestThumbnailImage() calls.
base::RepeatingClosure async_operation_finished_callback_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<ThumbnailImage> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(ThumbnailImage);
};
#endif // CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_