Home | History | Annotate | Download | only in cocoa
      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