chromium/ash/clipboard/clipboard_history.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_CLIPBOARD_CLIPBOARD_HISTORY_H_
#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_H_

#include <deque>
#include <list>

#include "ash/ash_export.h"
#include "ash/clipboard/clipboard_history_item.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/token.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/clipboard_observer.h"

namespace ash {
class ScopedClipboardHistoryPauseImpl;

namespace clipboard_history_util {
enum class PauseBehavior;
}  // namespace clipboard_history_util

// Keeps track of the last few things saved in the clipboard.
class ASH_EXPORT ClipboardHistory : public ui::ClipboardObserver {
 public:
  class ASH_EXPORT Observer : public base::CheckedObserver {
   public:
    // Called when a `ClipboardHistoryItem` has been added. `is_duplicate` is
    // true if `item` is already in clipboard history when adding.
    virtual void OnClipboardHistoryItemAdded(const ClipboardHistoryItem& item,
                                             bool is_duplicate) {}

    // Called when a ClipboardHistoryItem has been removed.
    virtual void OnClipboardHistoryItemRemoved(
        const ClipboardHistoryItem& item) {}

    // Called when ClipboardHistory is Clear()-ed.
    virtual void OnClipboardHistoryCleared() {}

    // Called when the operation on clipboard data is confirmed.
    virtual void OnOperationConfirmed(bool copy) {}
  };

  ClipboardHistory();
  ClipboardHistory(const ClipboardHistory&) = delete;
  ClipboardHistory& operator=(const ClipboardHistory&) = delete;
  ~ClipboardHistory() override;

  void AddObserver(Observer* observer) const;
  void RemoveObserver(Observer* observer) const;

  // Returns the list of most recent items. The returned list is sorted by
  // recency.
  const std::list<ClipboardHistoryItem>& GetItems() const;
  std::list<ClipboardHistoryItem>& GetItems();

  // Deletes every item in the clipboard history. The clipboard is cleared as
  // well to ensure that its contents stay in sync with the first item in the
  // clipboard history.
  void Clear();

  // Returns whether the clipboard history of the active account is empty.
  bool IsEmpty() const;

  // Remove the item specified by `id`. If the target item does not exist,
  // do nothing.
  void RemoveItemForId(const base::UnguessableToken& id);

  // ui::ClipboardObserver:
  void OnClipboardDataChanged() override;
  void OnClipboardDataRead() override;

  base::WeakPtr<ClipboardHistory> GetWeakPtr();

 private:
  // Friended to allow `ScopedClipboardHistoryPauseImpl` to `Pause()` and
  // `Resume()`.
  // TODO(b/269470292): Use a `PassKey` for this.
  friend class ScopedClipboardHistoryPauseImpl;

  // Ensures that the clipboard buffer contains the same data as the item at the
  // top of clipboard history. If clipboard history is empty, then the clipboard
  // is cleared.
  void SyncClipboardToClipboardHistory();

  // Adds `data` to the top of the history list if `data` is supported by
  // clipboard history. If `data` is not supported, this method no-ops. If
  // `data` is already in the history list, `data` will be moved to the top of
  // the list.
  void MaybeCommitData(ui::ClipboardData data, bool is_reorder_on_paste);

  // When `Pause()` is called, clipboard accesses will modify clipboard history
  // according to `pause_behavior` until `Resume()` is called with that pause's
  // `pause_id`. If `Pause()` is called while another pause is active, the
  // newest pause's behavior will be respected. When the newest pause ends, the
  // next newest pause's behavior will be restored.
  const base::Token& Pause(
      clipboard_history_util::PauseBehavior pause_behavior);
  void Resume(const base::Token& pause_id);
  struct PauseInfo {
    base::Token pause_id;
    clipboard_history_util::PauseBehavior pause_behavior;
  };

  // Keeps track of consecutive clipboard operations and records metrics.
  void OnClipboardOperation(bool copy);

  // Active clipboard history pauses, stored in LIFO order so that the newest
  // pause dictates behavior. Rather than a stack, we use a deque where the
  // newest pause is added to and removed from the front. Not using a stack
  // allows us to find and remove the correct pause in cases where pauses are
  // not destroyed in LIFO order, and adding to the front of the deque rather
  // than the back allows us to iterate forward when searching for the correct
  // pause, simplifying removal logic.
  std::deque<PauseInfo> pauses_;

  // The number of consecutive copies, reset after a paste.
  int consecutive_copies_ = 0;

  // The number of consecutive pastes, reset after a copy.
  int consecutive_pastes_ = 0;

  // The history of data copied to the Clipboard. Items of the list are sorted
  // by recency.
  std::list<ClipboardHistoryItem> history_list_;

  // Mutable to allow adding/removing from |observers_| through a const
  // ClipboardHistory.
  mutable base::ObserverList<Observer> observers_;

  // Factory to create WeakPtrs used to debounce calls to `CommitData()`.
  base::WeakPtrFactory<ClipboardHistory> commit_data_weak_factory_{this};

  // Factory to create WeakPtrs used to debounce calls to
  // `OnClipboardOperation()`.
  base::WeakPtrFactory<ClipboardHistory> clipboard_histogram_weak_factory_{
      this};

  // Factory to create WeakPtrs for ClipboardHistory.
  base::WeakPtrFactory<ClipboardHistory> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_CLIPBOARD_CLIPBOARD_HISTORY_H_