1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_UI_COCOA_HISTORY_MENU_BRIDGE_H_ 6 #define CHROME_BROWSER_UI_COCOA_HISTORY_MENU_BRIDGE_H_ 7 #pragma once 8 9 #import <Cocoa/Cocoa.h> 10 #include <map> 11 12 #include "base/memory/ref_counted.h" 13 #include "base/memory/scoped_nsobject.h" 14 #import "chrome/browser/favicon_service.h" 15 #include "chrome/browser/history/history.h" 16 #include "chrome/browser/sessions/session_id.h" 17 #include "chrome/browser/sessions/tab_restore_service.h" 18 #include "chrome/browser/sessions/tab_restore_service_observer.h" 19 #include "content/browser/cancelable_request.h" 20 #include "content/common/notification_observer.h" 21 22 class NavigationEntry; 23 class NotificationRegistrar; 24 class PageUsageData; 25 class Profile; 26 class TabNavigationEntry; 27 class TabRestoreService; 28 @class HistoryMenuCocoaController; 29 30 namespace { 31 32 class HistoryMenuBridgeTest; 33 34 } 35 36 // C++ bridge for the history menu; one per AppController (means there 37 // is only one). This class observes various data sources, namely the 38 // HistoryService and the TabRestoreService, and then updates the NSMenu when 39 // there is new data. 40 // 41 // The history menu is broken up into sections: most visisted and recently 42 // closed. The overall menu has a tag of IDC_HISTORY_MENU, with the user content 43 // items having the local tags defined in the enum below. Items within a section 44 // all share the same tag. The structure of the menu is laid out in MainMenu.xib 45 // and the generated content is inserted after the Title elements. The recently 46 // closed section is special in that those menu items can have submenus to list 47 // all the tabs within that closed window. By convention, these submenu items 48 // have a tag that's equal to the parent + 1. Tags within the history menu have 49 // a range of [400,500) and do not go through CommandDispatch for their target- 50 // action mechanism. 51 // 52 // These menu items do not use firstResponder as their target. Rather, they are 53 // hooked directly up to the HistoryMenuCocoaController that then bridges back 54 // to this class. These items are created via the AddItemToMenu() helper. Also, 55 // unlike the typical ownership model, this bridge owns its controller. The 56 // controller is very thin and only exists to interact with Cocoa, but this 57 // class does the bulk of the work. 58 class HistoryMenuBridge : public NotificationObserver, 59 public TabRestoreServiceObserver { 60 public: 61 // This is a generalization of the data we store in the history menu because 62 // we pull things from different sources with different data types. 63 struct HistoryItem { 64 public: 65 HistoryItem(); 66 // Copy constructor allowed. 67 HistoryItem(const HistoryItem& copy); 68 ~HistoryItem(); 69 70 // The title for the menu item. 71 string16 title; 72 // The URL that will be navigated to if the user selects this item. 73 GURL url; 74 // Favicon for the URL. 75 scoped_nsobject<NSImage> icon; 76 77 // If the icon is being requested from the FaviconService, |icon_requested| 78 // will be true and |icon_handle| will be non-NULL. If this is false, then 79 // |icon_handle| will be NULL. 80 bool icon_requested; 81 // The Handle given to us by the FaviconService for the icon fetch request. 82 FaviconService::Handle icon_handle; 83 84 // The pointer to the item after it has been created. Strong; NSMenu also 85 // retains this. During a rebuild flood (if the user closes a lot of tabs 86 // quickly), the NSMenu can release the item before the HistoryItem has 87 // been fully deleted. If this were a weak pointer, it would result in a 88 // zombie. 89 scoped_nsobject<NSMenuItem> menu_item; 90 91 // This ID is unique for a browser session and can be passed to the 92 // TabRestoreService to re-open the closed window or tab that this 93 // references. A non-0 session ID indicates that this is an entry can be 94 // restored that way. Otherwise, the URL will be used to open the item and 95 // this ID will be 0. 96 SessionID::id_type session_id; 97 98 // If the HistoryItem is a window, this will be the vector of tabs. Note 99 // that this is a list of weak references. The |menu_item_map_| is the owner 100 // of all items. If it is not a window, then the entry is a single page and 101 // the vector will be empty. 102 std::vector<HistoryItem*> tabs; 103 104 private: 105 // Copying is explicitly allowed, but assignment is not. 106 void operator=(const HistoryItem&); 107 }; 108 109 // These tags are not global view tags and are local to the history menu. The 110 // normal procedure for menu items is to go through CommandDispatch, but since 111 // history menu items are hooked directly up to their target, they do not need 112 // to have the global IDC view tags. 113 enum Tags { 114 kMostVisitedSeparator = 400, // Separator before most visited section. 115 kMostVisitedTitle = 401, // Title of the most visited section. 116 kMostVisited = 420, // Used for all entries in the most visited section. 117 kRecentlyClosedSeparator = 440, // Item before recently closed section. 118 kRecentlyClosedTitle = 441, // Title of recently closed section. 119 kRecentlyClosed = 460, // Used for items in the recently closed section. 120 kShowFullSeparator = 480 // Separator after the recently closed section. 121 }; 122 123 explicit HistoryMenuBridge(Profile* profile); 124 virtual ~HistoryMenuBridge(); 125 126 // Overriden from NotificationObserver. 127 virtual void Observe(NotificationType type, 128 const NotificationSource& source, 129 const NotificationDetails& details); 130 131 // For TabRestoreServiceObserver 132 virtual void TabRestoreServiceChanged(TabRestoreService* service); 133 virtual void TabRestoreServiceDestroyed(TabRestoreService* service); 134 135 // Looks up an NSMenuItem in the |menu_item_map_| and returns the 136 // corresponding HistoryItem. 137 HistoryItem* HistoryItemForMenuItem(NSMenuItem* item); 138 139 // I wish I has a "friend @class" construct. These are used by the HMCC 140 // to access model information when responding to actions. 141 HistoryService* service(); 142 Profile* profile(); 143 144 protected: 145 // Return the History menu. 146 virtual NSMenu* HistoryMenu(); 147 148 // Clear items in the given |menu|. Menu items in the same section are given 149 // the same tag. This will go through the entire history menu, removing all 150 // items with a given tag. Note that this will recurse to submenus, removing 151 // child items from the menu item map. This will only remove items that have 152 // a target hooked up to the |controller_|. 153 void ClearMenuSection(NSMenu* menu, NSInteger tag); 154 155 // Adds a given title and URL to the passed-in menu with a certain tag and 156 // index. This will add |item| and the newly created menu item to the 157 // |menu_item_map_|, which takes ownership. Items are deleted in 158 // ClearMenuSection(). This returns the new menu item that was just added. 159 NSMenuItem* AddItemToMenu(HistoryItem* item, 160 NSMenu* menu, 161 NSInteger tag, 162 NSInteger index); 163 164 // Called by the ctor if |service_| is ready at the time, or by a 165 // notification receiver. Finishes initialization tasks by subscribing for 166 // change notifications and calling CreateMenu(). 167 void Init(); 168 169 // Does the query for the history information to create the menu. 170 void CreateMenu(); 171 172 // Callback method for when HistoryService query results are ready with the 173 // most recently-visited sites. 174 void OnVisitedHistoryResults(CancelableRequestProvider::Handle handle, 175 std::vector<PageUsageData*>* results); 176 177 // Creates a HistoryItem* for the given tab entry. Caller takes ownership of 178 // the result and must delete it when finished. 179 HistoryItem* HistoryItemForTab(const TabRestoreService::Tab& entry); 180 181 // Helper function that sends an async request to the FaviconService to get 182 // an icon. The callback will update the NSMenuItem directly. 183 void GetFaviconForHistoryItem(HistoryItem* item); 184 185 // Callback for the FaviconService to return favicon image data when we 186 // request it. This decodes the raw data, updates the HistoryItem, and then 187 // sets the image on the menu. Called on the same same thread that 188 // GetFaviconForHistoryItem() was called on (UI thread). 189 void GotFaviconData(FaviconService::Handle handle, 190 history::FaviconData favicon); 191 192 // Cancels a favicon load request for a given HistoryItem, if one is in 193 // progress. 194 void CancelFaviconRequest(HistoryItem* item); 195 196 private: 197 friend class ::HistoryMenuBridgeTest; 198 friend class HistoryMenuCocoaControllerTest; 199 200 scoped_nsobject<HistoryMenuCocoaController> controller_; // strong 201 202 Profile* profile_; // weak 203 HistoryService* history_service_; // weak 204 TabRestoreService* tab_restore_service_; // weak 205 206 NotificationRegistrar registrar_; 207 CancelableRequestConsumer cancelable_request_consumer_; 208 209 // Mapping of NSMenuItems to HistoryItems. This owns the HistoryItems until 210 // they are removed and deleted via ClearMenuSection(). 211 std::map<NSMenuItem*, HistoryItem*> menu_item_map_; 212 213 // Maps HistoryItems to favicon request Handles. 214 CancelableRequestConsumerTSimple<HistoryItem*> favicon_consumer_; 215 216 // Requests to re-create the menu are coalesced. |create_in_progress_| is true 217 // when either waiting for the history service to return query results, or 218 // when the menu is rebuilding. |need_recreate_| is true whenever a rebuild 219 // has been scheduled but is waiting for the current one to finish. 220 bool create_in_progress_; 221 bool need_recreate_; 222 223 // The default favicon if a HistoryItem does not have one. 224 scoped_nsobject<NSImage> default_favicon_; 225 226 DISALLOW_COPY_AND_ASSIGN(HistoryMenuBridge); 227 }; 228 229 #endif // CHROME_BROWSER_UI_COCOA_HISTORY_MENU_BRIDGE_H_ 230