blob: fe6253e16150600b1e15dc5952f41fbf8555b762 [file] [log] [blame]
// 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_