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
|
// 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_BOOKMARKS_BOOKMARK_MENU_BRIDGE_H_
#define CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_MENU_BRIDGE_H_
#include <map>
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/bookmarks/bookmark_merged_surface_service_observer.h"
#include "components/bookmarks/browser/bookmark_model.h"
class BookmarkMergedSurfaceService;
struct BookmarkParentFolder;
class Profile;
@class NSImage;
@class NSMenu;
@class NSMenuItem;
@class BookmarkMenuCocoaController;
namespace bookmarks {
class BookmarkNode;
}
// C++ controller for the bookmark menu; one per AppController (which
// means there is only one). When bookmarks are changed, this class
// takes care of updating Cocoa bookmark menus. This is not named
// BookmarkMenuController to help avoid confusion between languages.
// This class needs to be C++, not ObjC, since it derives from
// BookmarkMergedSurfaceServiceObserver.
//
// Most Chromium Cocoa menu items are static from a nib (e.g. New
// Tab), but may be enabled/disabled under certain circumstances
// (e.g. Cut and Paste). In addition, most Cocoa menu items have
// firstResponder: as a target. Unusually, bookmark menu items are
// created dynamically. They also have a target of
// BookmarkMenuCocoaController instead of firstResponder.
// See BookmarkMenuBridge::AddNodeToMenu()).
class BookmarkMenuBridge : public BookmarkMergedSurfaceServiceObserver {
public:
BookmarkMenuBridge(Profile* profile, NSMenu* menu_root);
BookmarkMenuBridge(const BookmarkMenuBridge&) = delete;
BookmarkMenuBridge& operator=(const BookmarkMenuBridge&) = delete;
~BookmarkMenuBridge() override;
// BookmarkMergedSurfaceServiceObserver:
void BookmarkMergedSurfaceServiceLoaded() override;
void BookmarkMergedSurfaceServiceBeingDeleted() override;
void BookmarkNodeAdded(const BookmarkParentFolder& parent,
size_t index) override;
void BookmarkNodesRemoved(
const BookmarkParentFolder& parent,
const base::flat_set<const bookmarks::BookmarkNode*>& nodes) override;
void BookmarkNodeMoved(const BookmarkParentFolder& old_parent,
size_t old_index,
const BookmarkParentFolder& new_parent,
size_t new_index) override;
void BookmarkNodeChanged(const bookmarks::BookmarkNode* node) override;
void BookmarkNodeFaviconChanged(const bookmarks::BookmarkNode* node) override;
void BookmarkParentFolderChildrenReordered(
const BookmarkParentFolder& folder) override;
void BookmarkAllUserNodesRemoved() override;
bool IsMenuRoot(NSMenu* menu);
// Builds the main bookmark menu if it has been marked invalid. Its submenus
// will NOT be built recursively.
void UpdateRootMenuIfInvalid();
// Builds a bookmark folder submenu on demand. Submenus of `menu` will NOT be
// built recursively.
void UpdateNonRootMenu(NSMenu* menu, const BookmarkParentFolder& folder);
// I wish I had a "friend @class" construct.
bookmarks::BookmarkModel* GetBookmarkModelForTesting();
Profile* GetProfile();
const base::FilePath& GetProfileDir() const;
// Return the Bookmark menu.
NSMenu* BookmarkMenu();
// Clear all bookmarks from |menu_root_|.
void ClearBookmarkMenu();
// Resets |profile_| to nullptr. Called before the Profile is destroyed, if
// this bridge is still needed. Rebuilds the entire menu recursively, so it
// remains functional after the Profile is destroyed.
//
// Also performs some internal cleanup, like resetting observers and pointers
// to the Profile and KeyedServices.
void OnProfileWillBeDestroyed();
// Returns the GUID of the BookmarkNode for |tag|. If |tag| is not the tag of
// an NSMenuItem in this menu, returns the invalid GUID.
base::Uuid TagToGUID(int64_t tag) const;
// Returns the NSMenuItem for a given BookmarkNode, exposed publicly for
// testing.
NSMenuItem* MenuItemForNodeForTest(const bookmarks::BookmarkNode* node);
private:
friend class BookmarkMenuBridgeTest;
// Returns true if the parent folder has at least one child.
bool HasContent(const BookmarkParentFolder& folder);
void BuildRootMenu(bool recurse);
// Marks the bookmark menu as being invalid.
void InvalidateMenu() { is_menu_valid_ = false; }
bool IsMenuValid() const { return is_menu_valid_; }
// Adds a submenu representing |folder| to |menu|. Uses the title of
// |folder|'s underlying nodes as the submenu's title and the provided |image|
// as its icon. If |recurse| is true, recursively adds all child nodes of
// |node|.
void AddSubmenu(NSMenu* menu,
const BookmarkParentFolder& folder,
NSImage* image,
bool recurse);
// Adds all child nodes of |folder| to |menu|. If |recurse| is true,
// recursively adds children of the child nodes.
void AddChildrenToMenu(const BookmarkParentFolder& folder,
NSMenu* menu,
bool recurse);
// Adds |node| as an item or a submenu to the bookmark menu. If |recurse| is
// true and |node| has children, recursively adds them.
//
// TODO(jrg): add a counter to enforce maximum nodes added
void AddNodeToMenu(const bookmarks::BookmarkNode* node,
NSMenu* menu,
bool recurse);
// Helper for adding an item to our bookmark menu. An item which has a
// localized title specified by |message_id| will be added to |menu|.
// The item is also bound to |node| by tag. |command_id| selects the action.
void AddItemToMenu(int command_id,
int message_id,
const bookmarks::BookmarkNode* node,
NSMenu* menu,
bool enabled);
// This configures an NSMenuItem with all the data from a BookmarkNode. This
// is used to update existing menu items, as well as to configure newly
// created ones, like in AddNodeToMenu().
void ConfigureMenuItem(const bookmarks::BookmarkNode* node, NSMenuItem* item);
// Returns the NSMenuItem for a given BookmarkNode.
NSMenuItem* MenuItemForNode(const bookmarks::BookmarkNode* node);
// True iff the menu is up to date with the BookmarkMergedSurfaceService.
bool is_menu_valid_;
raw_ptr<Profile> profile_; // weak
raw_ptr<BookmarkMergedSurfaceService>
bookmark_service_; // owned by |profile_|.
BookmarkMenuCocoaController* __strong controller_;
NSMenu* __strong menu_root_;
base::FilePath profile_dir_; // Remembered after OnProfileWillBeDestroyed().
// The folder image so we can use one copy for all.
NSImage* __strong folder_image_;
// In order to appropriately update items in the bookmark menu, without
// forcing a rebuild, map the model's nodes to menu items.
std::map<const bookmarks::BookmarkNode*, NSMenuItem*> bookmark_nodes_;
// Tags are NSIntegers, so they're not necessarily large enough to hold a
// GUID. Instead, map the tags to the corresponding GUIDs.
std::map<int64_t, base::Uuid> tag_to_guid_;
base::ScopedObservation<BookmarkMergedSurfaceService,
BookmarkMergedSurfaceServiceObserver>
bookmark_service_observation_{this};
};
#endif // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_MENU_BRIDGE_H_
|