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