1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
|
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COCOA_HISTORY_MENU_BRIDGE_H_
#define CHROME_BROWSER_UI_COCOA_HISTORY_MENU_BRIDGE_H_
#import <Cocoa/Cocoa.h>
#include <map>
#include <memory>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/task/cancelable_task_tracker.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_manager_observer.h"
#import "chrome/browser/ui/cocoa/main_menu_item.h"
#import "components/favicon/core/favicon_service.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/tab_restore_service.h"
#include "components/sessions/core/tab_restore_service_observer.h"
class Profile;
class ScopedProfileKeepAlive;
@class HistoryMenuCocoaController;
namespace {
class HistoryMenuBridgeTest;
class HistoryMenuBridgeLifetimeTest;
} // namespace
namespace favicon_base {
struct FaviconImageResult;
}
// C++ bridge for the history menu; one per AppController (means there
// is only one). This class observes various data sources, namely the
// HistoryService and the TabRestoreService, and then updates the NSMenu when
// there is new data.
//
// The history menu is broken up into sections: recently visited and recently
// closed. The overall menu has a tag of IDC_HISTORY_MENU, with the user content
// items having the local tags defined in the enum below. Items within a section
// all share the same tag. The structure of the menu is laid out in MainMenu.xib
// and the generated content is inserted after the Title elements. The recently
// closed section is special in that those menu items can have submenus to list
// all the tabs within that closed window. By convention, these submenu items
// have a tag that's equal to the parent + 1. Tags within the history menu have
// a range of [400,500) and do not go through CommandDispatch for their target-
// action mechanism.
//
// These menu items do not use firstResponder as their target. Rather, they are
// hooked directly up to the HistoryMenuCocoaController that then bridges back
// to this class. These items are created via the AddItemToMenu() helper. Also,
// unlike the typical ownership model, this bridge owns its controller. The
// controller is very thin and only exists to interact with Cocoa, but this
// class does the bulk of the work.
class HistoryMenuBridge : public sessions::TabRestoreServiceObserver,
public MainMenuItem,
public history::HistoryServiceObserver,
public ProfileManagerObserver {
public:
// This is a generalization of the data we store in the history menu because
// we pull things from different sources with different data types.
struct HistoryItem {
public:
HistoryItem();
// Copy constructor allowed.
HistoryItem(const HistoryItem& copy);
~HistoryItem();
// The title for the menu item.
std::u16string title;
// The URL that will be navigated to if the user selects this item.
GURL url;
// Favicon for the URL.
NSImage* __strong icon;
// If the icon is being requested from the FaviconService, |icon_requested|
// will be true and |icon_task_id| will be valid. If this is false, then
// |icon_task_id| will be
// base::CancelableTaskTracker::kBadTaskId.
bool icon_requested = false;
// The Handle given to us by the FaviconService for the icon fetch request.
base::CancelableTaskTracker::TaskId icon_task_id =
base::CancelableTaskTracker::kBadTaskId;
// The pointer to the item after it has been created. Strong; NSMenu also
// retains this. During a rebuild flood (if the user closes a lot of tabs
// quickly), the NSMenu can release the item before the HistoryItem has
// been fully deleted. If this were a weak pointer, it would result in a
// zombie.
NSMenuItem* __strong menu_item;
// This ID is unique for a browser session and can be passed to the
// TabRestoreService to re-open the closed window or tab that this
// references. A valid session ID indicates that this is an entry can be
// restored that way. Otherwise, the URL will be used to open the item and
// this ID will be SessionID::InvalidValue().
SessionID session_id;
// If the HistoryItem is a window, this will be the vector of tabs. Note
// that this is a list of weak references. The |menu_item_map_| is the owner
// of all items. If it is not a window, then the entry is a single page and
// the vector will be empty.
std::vector<HistoryItem*> tabs;
private:
// Copying is explicitly allowed, but assignment is not.
void operator=(const HistoryItem&);
};
// These tags are not global view tags and are local to the history menu. The
// normal procedure for menu items is to go through CommandDispatch, but since
// history menu items are hooked directly up to their target, they do not need
// to have the global IDC view tags.
enum Tags {
kRecentlyClosedSeparator = 400, // Item before recently closed section.
kRecentlyClosedTitle = 401, // Title of recently closed section.
kRecentlyClosed = 420, // Used for items in the recently closed section.
kVisitedSeparator = 440, // Separator before visited section.
kVisitedTitle = 441, // Title of the visited section.
kVisited = 460, // Used for all entries in the visited section.
kShowFullSeparator = 480 // Separator after the visited section.
};
explicit HistoryMenuBridge(Profile* profile);
HistoryMenuBridge(const HistoryMenuBridge&) = delete;
HistoryMenuBridge& operator=(const HistoryMenuBridge&) = delete;
~HistoryMenuBridge() override;
// TabRestoreServiceObserver:
void TabRestoreServiceChanged(sessions::TabRestoreService* service) override;
void TabRestoreServiceDestroyed(
sessions::TabRestoreService* service) override;
void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override;
// MainMenuItem:
void ResetMenu() override;
void BuildMenu() override;
// ProfileManagerObserver:
void OnProfileMarkedForPermanentDeletion(Profile* profile) override;
void OnProfileManagerDestroying() override;
// Looks up an NSMenuItem in the |menu_item_map_| and returns the
// corresponding HistoryItem.
HistoryItem* HistoryItemForMenuItem(NSMenuItem* item);
// Called by HistoryMenuCocoaController when the menu begins and ends
// tracking, to block updates when it is open.
void SetIsMenuOpen(bool flag);
// I wish I has a "friend @class" construct. These are used by the HMCC
// to access model information when responding to actions.
history::HistoryService* service();
Profile* profile();
const base::FilePath& profile_dir() const;
// Resets |profile_| to nullptr. Called before the Profile is destroyed, when
// this bridge is still needed. Also performs some internal cleanup, like
// resetting observers and pointers to the Profile and KeyedServices.
void OnProfileWillBeDestroyed();
protected:
// Return the History menu.
virtual NSMenu* HistoryMenu();
// Clear items in the given |menu|. Menu items in the same section are given
// the same tag. This will go through the entire history menu, removing all
// items with a given tag. Note that this will recurse to submenus, removing
// child items from the menu item map. This will only remove items that have
// a target hooked up to the |controller_|.
void ClearMenuSection(NSMenu* menu, NSInteger tag);
// Adds a given title and URL to the passed-in menu with a certain tag and
// index. This will add |item| and the newly created menu item to the
// |menu_item_map_|, which takes ownership. Items are deleted in
// ClearMenuSection(). This returns the new menu item that was just added.
NSMenuItem* AddItemToMenu(std::unique_ptr<HistoryItem> item,
NSMenu* menu,
NSInteger tag,
NSInteger index);
// Adds an item for the window entry with a submenu containing its tabs.
// Returns whether the item was successfully added.
bool AddWindowEntryToMenu(sessions::tab_restore::Window* window,
NSMenu* menu,
NSInteger tag,
NSInteger index);
// Adds an item for the group entry with a submenu containing its tabs.
// Returns whether the item was successfully added.
bool AddGroupEntryToMenu(sessions::tab_restore::Group* group,
NSMenu* menu,
NSInteger tag,
NSInteger index);
// Adds standard 'Restore All' items and an item for each tab in |tabs|,
// potentially filtering out tabs like the NTP. Returns the number of tabs
// successfully added and updates the HistoryItem with those tabs.
int AddTabsToSubmenu(
NSMenu* submenu,
HistoryItem* item,
const std::vector<std::unique_ptr<sessions::tab_restore::Tab>>& tabs);
// Called by the ctor if |service_| is ready at the time, or by a
// notification receiver. Finishes initialization tasks by subscribing for
// change notifications and calling CreateMenu().
void Init();
// Does the query for the history information to create the menu.
void CreateMenu();
// Invoked when the History information has changed.
void OnHistoryChanged();
// Callback method for when HistoryService query results are ready with the
// most recently-visited sites.
void OnVisitedHistoryResults(history::QueryResults results);
// Creates a HistoryItem* for the given tab entry.
std::unique_ptr<HistoryItem> HistoryItemForTab(
const sessions::tab_restore::Tab& entry);
// Helper function that sends an async request to the FaviconService to get
// an icon. The callback will update the NSMenuItem directly.
void GetFaviconForHistoryItem(HistoryItem* item);
// Callback for the FaviconService to return favicon image data when we
// request it. This decodes the raw data, updates the HistoryItem, and then
// sets the image on the menu. Called on the same same thread that
// GetFaviconForHistoryItem() was called on (UI thread).
void GotFaviconData(HistoryItem* item,
const favicon_base::FaviconImageResult& image_result);
// Cancels a favicon load request for a given HistoryItem, if one is in
// progress.
void CancelFaviconRequest(HistoryItem* item);
private:
friend class ::HistoryMenuBridgeTest;
friend class ::HistoryMenuBridgeLifetimeTest;
friend class HistoryMenuCocoaControllerTest;
void FinishCreateMenu();
// history::HistoryServiceObserver:
void OnURLVisited(history::HistoryService* history_service,
const history::URLRow& url_row,
const history::VisitRow& new_visit) override;
void OnURLsModified(history::HistoryService* history_service,
const history::URLRows& changed_urls) override;
void OnHistoryDeletions(history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) override;
void OnHistoryServiceLoaded(history::HistoryService* service) override;
void HistoryServiceBeingDeleted(history::HistoryService* service) override;
// Changes the visibility of the menu items depend on the current profile
// type.
void SetVisibilityOfMenuItems();
// Returns if the given menu item should be visible for the current profile.
bool ShouldMenuItemBeVisible(NSMenuItem* item);
HistoryMenuCocoaController* __strong controller_;
raw_ptr<Profile> profile_; // weak
raw_ptr<history::HistoryService> history_service_ = nullptr; // weak
raw_ptr<sessions::TabRestoreService> tab_restore_service_ = nullptr; // weak
base::FilePath profile_dir_; // Remembered after OnProfileWillBeDestroyed().
// Inhibit Profile destruction until the HistoryService and TabRestoreService
// are finished loading.
std::unique_ptr<ScopedProfileKeepAlive> history_service_keep_alive_;
std::unique_ptr<ScopedProfileKeepAlive> tab_restore_service_keep_alive_;
base::CancelableTaskTracker cancelable_task_tracker_;
// A timer used to coalesce repeated calls to CreateMenu().
base::OneShotTimer finish_create_menu_timer_;
// Mapping of NSMenuItems to HistoryItems.
std::map<NSMenuItem*, std::unique_ptr<HistoryItem>> menu_item_map_;
// Requests to re-create the menu are coalesced. |create_in_progress_| is true
// when either waiting for the history service to return query results, or
// when the menu is rebuilding. |need_recreate_| is true whenever a rebuild
// has been scheduled but is waiting for the current one to finish.
bool create_in_progress_ = false;
bool need_recreate_ = false;
// In order to not jarringly refresh the menu while the user has it open,
// updates are blocked while the menu is tracking.
bool is_menu_open_ = false;
// The default favicon if a HistoryItem does not have one.
NSImage* __strong default_favicon_;
base::ScopedObservation<history::HistoryService,
history::HistoryServiceObserver>
history_service_observation_{this};
base::ScopedObservation<sessions::TabRestoreService,
sessions::TabRestoreServiceObserver>
tab_restore_service_observation_{this};
base::ScopedObservation<ProfileManager, ProfileManagerObserver>
profile_manager_observation_{this};
base::WeakPtrFactory<HistoryMenuBridge> weak_factory_{this};
};
#endif // CHROME_BROWSER_UI_COCOA_HISTORY_MENU_BRIDGE_H_
|