blob: b5f8984d2ac54aabcc3e6d50226e1c1ac69067f8 [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_CONTROLLER_IMPL_H_
#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_CONTROLLER_IMPL_H_
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "ash/ash_export.h"
#include "ash/clipboard/clipboard_history.h"
#include "ash/clipboard/clipboard_history_resource_manager.h"
#include "ash/public/cpp/clipboard_history_controller.h"
#include "ash/public/cpp/session/session_observer.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/one_shot_event.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/unguessable_token.h"
#include "base/values.h"
#include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
class PrefRegistrySimple;
namespace aura {
class Window;
} // namespace aura
namespace gfx {
class Rect;
} // namespace gfx
namespace ash {
class ClipboardHistoryControllerDelegate;
class ClipboardHistoryItem;
class ClipboardHistoryMenuModelAdapter;
class ClipboardHistoryResourceManager;
class ClipboardHistoryUrlTitleFetcher;
class ClipboardImageModelFactory;
class ClipboardNudgeController;
class ScopedClipboardHistoryPause;
enum class LoginStatus;
// Shows a menu with the last few things saved in the clipboard when the
// keyboard shortcut is pressed.
class ASH_EXPORT ClipboardHistoryControllerImpl
: public ClipboardHistoryController,
public ClipboardHistory::Observer,
public ClipboardHistoryResourceManager::Observer,
public SessionObserver {
public:
// Source and plain vs. rich text info for each paste. These values are used
// in the Ash.ClipboardHistory.PasteType histogram and therefore cannot be
// reordered. New types may be appended to the end of this enumeration.
enum class ClipboardHistoryPasteType {
kPlainTextAccelerator = 0, // Plain text paste triggered by accelerator
kRichTextAccelerator = 1, // Rich text paste triggered by accelerator
kPlainTextKeystroke = 2, // Plain text paste triggered by keystroke
kRichTextKeystroke = 3, // Rich text paste triggered by keystroke
kPlainTextMouse = 4, // Plain text paste triggered by mouse click
kRichTextMouse = 5, // Rich text paste triggered by mouse click
kPlainTextTouch = 6, // Plain text paste triggered by gesture tap
kRichTextTouch = 7, // Rich text paste triggered by gesture tap
kPlainTextVirtualKeyboard = 8, // Plain text paste triggered by VK request
kRichTextVirtualKeyboard = 9, // Rich text paste triggered by VK request
kPlainTextCtrlV = 10, // Plain text paste triggered by Ctrl+V
kRichTextCtrlV = 11, // Rich text paste triggered by Ctrl+V
kMaxValue = 11
};
explicit ClipboardHistoryControllerImpl(
std::unique_ptr<ClipboardHistoryControllerDelegate> delegate);
ClipboardHistoryControllerImpl(const ClipboardHistoryControllerImpl&) =
delete;
ClipboardHistoryControllerImpl& operator=(
const ClipboardHistoryControllerImpl&) = delete;
~ClipboardHistoryControllerImpl() override;
// Registers clipboard history profile prefs with the specified `registry`.
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// Clean up the child widgets prior to destruction.
void Shutdown();
// Returns if the contextual menu is currently showing.
bool IsMenuShowing() const;
// Shows or hides the clipboard history menu through the keyboard accelerator.
// If the menu was already shown, pastes the selected menu item before hiding.
// If the menu was not already shown and `is_plain_text_paste` is true the
// menu will not be shown. The common case for `is_plain_text_paste` is to
// allow pasting plain text when menu is already open, otherwise do not allow
// the plain text shortcut to open the menu.
void ToggleMenuShownByAccelerator(bool is_plain_text_paste);
// ClipboardHistoryController:
void AddObserver(ClipboardHistoryController::Observer* observer) override;
void RemoveObserver(ClipboardHistoryController::Observer* observer) override;
bool ShowMenu(const gfx::Rect& anchor_rect,
ui::MenuSourceType source_type,
crosapi::mojom::ClipboardHistoryControllerShowSource
show_source) override;
bool ShowMenu(
const gfx::Rect& anchor_rect,
ui::MenuSourceType source_type,
crosapi::mojom::ClipboardHistoryControllerShowSource show_source,
OnMenuClosingCallback callback) override;
void GetHistoryValues(GetHistoryValuesCallback callback) const override;
// Whether the clipboard history has items.
bool IsEmpty() const;
// Fires the timer to notify observers of item updates immediately.
void FireItemUpdateNotificationTimerForTest();
// Returns bounds for the contextual menu in screen coordinates.
gfx::Rect GetMenuBoundsInScreenForTest() const;
// Used to delay the post-encoding step of `GetHistoryValues()` until the
// completion of some work that needs to happen after history values have been
// requested and before the values are returned.
void BlockGetHistoryValuesForTest();
void ResumeGetHistoryValuesForTest();
// Returns the history which tracks what is being copied to the clipboard.
const ClipboardHistory* history() const { return clipboard_history_.get(); }
// Returns the resource manager which gets labels and images for items copied
// to the clipboard.
const ClipboardHistoryResourceManager* resource_manager() const {
return resource_manager_.get();
}
ClipboardNudgeController* nudge_controller() const {
return nudge_controller_.get();
}
ClipboardHistoryMenuModelAdapter* context_menu_for_test() {
return context_menu_.get();
}
void set_buffer_restoration_delay_for_test(
std::optional<base::TimeDelta> delay) {
buffer_restoration_delay_for_test_ = delay;
}
void set_initial_item_selected_callback_for_test(
base::RepeatingClosure new_callback) {
initial_item_selected_callback_for_test_ = new_callback;
}
void set_confirmed_operation_callback_for_test(
base::RepeatingCallback<void(bool)> new_callback) {
confirmed_operation_callback_for_test_ = new_callback;
}
void set_new_bitmap_to_write_while_encoding_for_test(const SkBitmap& bitmap) {
new_bitmap_to_write_while_encoding_for_test_ = bitmap;
}
private:
class AcceleratorTarget;
class MenuDelegate;
// ClipboardHistoryController:
bool HasAvailableHistoryItems() const override;
void OnScreenshotNotificationCreated() override;
std::unique_ptr<ScopedClipboardHistoryPause> CreateScopedPause() override;
std::vector<std::string> GetHistoryItemIds() const override;
bool PasteClipboardItemById(
const std::string& item_id,
int event_flags,
crosapi::mojom::ClipboardHistoryControllerShowSource paste_source)
override;
bool DeleteClipboardItemById(const std::string& item_id) override;
// ClipboardHistory::Observer:
void OnClipboardHistoryItemAdded(const ClipboardHistoryItem& item,
bool is_duplicate) override;
void OnClipboardHistoryItemRemoved(const ClipboardHistoryItem& item) override;
void OnClipboardHistoryCleared() override;
void OnOperationConfirmed(bool copy) override;
// ClipboardHistoryResourceManager:
void OnCachedImageModelUpdated(
const std::vector<base::UnguessableToken>& menu_item_ids) override;
// SessionObserver:
void OnSessionStateChanged(session_manager::SessionState state) override;
void OnLoginStatusChanged(LoginStatus login_status) override;
// Posts a task to notify `observers_` of updates to clipboard history items.
void PostItemUpdateNotificationTask();
// Notifies `observers_` of updates to clipboard history items. No-op if
// there are no available clipboard history items and there were no available
// history items in the last notification.
void MaybeNotifyObserversOfItemUpdate();
// Invoked by `GetHistoryValues()` once all clipboard instances with images
// have been encoded into PNGs. Calls `callback` with the clipboard history
// list, which tracks what has been copied to the clipboard. If clipboard
// history is disabled in the current mode, `callback` will be called with an
// empty history list.
void GetHistoryValuesWithEncodedPNGs(
GetHistoryValuesCallback callback,
std::unique_ptr<std::map<base::UnguessableToken, std::vector<uint8_t>>>
encoded_pngs);
// Executes the command specified by `command_id` with the given
// `event_flags`.
void ExecuteCommand(int command_id, int event_flags);
// Pastes the clipboard data of the clipboard history context menu item
// specified by `command_id`. NOTE: This function assumes that the clipboard
// history context menu has been open. It is different from
// `PasteClipboardItemById()` that can be called without showing the clipboard
// history context menu.
void PasteClipboardItemByCommandId(int command_id,
ClipboardHistoryPasteType paste_type);
// Posts a task to paste `item` with `paste_type` to the active window, if
// any.
void MaybePostPasteTask(
const ClipboardHistoryItem& item,
ClipboardHistoryPasteType paste_type,
crosapi::mojom::ClipboardHistoryControllerShowSource paste_source);
// Pastes the specified clipboard history item, if `intended_window` matches
// the active window. `paste_type` indicates the mode of paste execution for
// metrics tracking as well as whether plain text should be pasted instead of
// the full, rich-text clipboard data. `paste_source` indicates how the user
// triggered the menu from which `item` was selected.
void PasteClipboardHistoryItem(
aura::Window* intended_window,
ClipboardHistoryItem item,
ClipboardHistoryPasteType paste_type,
crosapi::mojom::ClipboardHistoryControllerShowSource paste_source);
// Delete the menu item being selected and its corresponding data. If no item
// is selected, do nothing.
void DeleteSelectedMenuItemIfAny();
// Delete the menu item specified by `command_id` and its corresponding data.
void DeleteItemWithCommandId(int command_id);
// Deletes the specified clipboard history item.
void DeleteClipboardHistoryItem(const ClipboardHistoryItem& item);
// Advances the pseudo focus (backward if `reverse` is true).
void AdvancePseudoFocus(bool reverse);
// Calculates the anchor rect for the ClipboardHistory menu.
gfx::Rect CalculateAnchorRect() const;
// Called when the contextual menu is closed.
void OnMenuClosed();
// Either the browser-implemented or test-implemented delegate depending on
// whether we are running in an Ash-only test context.
const std::unique_ptr<ClipboardHistoryControllerDelegate> delegate_;
// The browser-implemented image model factory that renders html. This will be
// `nullptr` if and only if we are running in an Ash-only test context.
const std::unique_ptr<ClipboardImageModelFactory> image_model_factory_;
// The browser-implemented URL title fetcher. This will be `nullptr` if and
// only if we are running in an Ash-only test context.
const std::unique_ptr<ClipboardHistoryUrlTitleFetcher> url_title_fetcher_;
// Observers notified when clipboard history is shown, used, or updated.
base::ObserverList<ClipboardHistoryController::Observer> observers_;
// Used to keep track of what is being copied to the clipboard.
std::unique_ptr<ClipboardHistory> clipboard_history_;
// Manages resources for clipboard history.
std::unique_ptr<ClipboardHistoryResourceManager> resource_manager_;
// Detects the search+v key combo.
std::unique_ptr<AcceleratorTarget> accelerator_target_;
// Controller that shows contextual nudges for multipaste.
std::unique_ptr<ClipboardNudgeController> nudge_controller_;
// Context menu displayed by `ShowMenu()`. Null when `MenuIsShowing()` is
// false.
std::unique_ptr<ClipboardHistoryMenuModelAdapter> context_menu_;
// Handles events on the `context_menu_`.
std::unique_ptr<MenuDelegate> menu_delegate_;
// How the user last caused the `context_menu_` to show.
crosapi::mojom::ClipboardHistoryControllerShowSource last_menu_source_;
// Indicates whether the clipboard data has been replaced due to an
// in-progress clipboard history paste.
bool clipboard_data_replaced_ = false;
// Used to post asynchronous tasks when opening or closing the clipboard
// history menu. Note that those tasks have data races between each other.
// The timer can guarantee that at most one task is alive.
base::OneShotTimer menu_task_timer_;
// Indicates the count of pastes which are triggered through the clipboard
// history menu and are waiting for the confirmations from `ClipboardHistory`.
int pastes_to_be_confirmed_ = 0;
// Used to post a task to notify `observers_` of updates to clipboard history
// items.
base::OneShotTimer item_update_notification_timer_;
// True if there were available items in the last clipboard history update
// notification.
bool has_available_items_in_last_update_ = false;
// Created when a test requests that `GetHistoryValues()` wait for some work
// to be done before encoding finishes. Reset and recreated if the same test
// makes the request to pause `GetHistoryValues()` again.
std::unique_ptr<base::OneShotEvent> get_history_values_blocker_for_test_;
// The delay interval for restoring the clipboard buffer to its original
// state following a paste event.
std::optional<base::TimeDelta> buffer_restoration_delay_for_test_;
// Called when the first item view is selected after the clipboard history
// menu opens.
base::RepeatingClosure initial_item_selected_callback_for_test_;
// Called when a copy or paste finishes. Accepts the operation's success as an
// argument.
base::RepeatingCallback<void(bool)> confirmed_operation_callback_for_test_;
// A new bitmap to be written to the clipboard while existing images are being
// encoded during `GetHistoryValues()`, which will force `GetHistoryValues()`
// to re-run in order to encode this new bitmap. This member is marked mutable
// so it can be cleared once it has been written to the clipboard.
mutable SkBitmap new_bitmap_to_write_while_encoding_for_test_;
base::WeakPtrFactory<ClipboardHistoryControllerImpl> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_CLIPBOARD_CLIPBOARD_HISTORY_CONTROLLER_IMPL_H_