| // 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_ |