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_TOOLBAR_BACK_FORWARD_MENU_MODEL_H_
6#define CHROME_BROWSER_UI_TOOLBAR_BACK_FORWARD_MENU_MODEL_H_
7#pragma once
8
9#include <set>
10#include <string>
11
12#include "base/basictypes.h"
13#include "base/gtest_prod_util.h"
14#include "base/string16.h"
15#include "chrome/browser/favicon_service.h"
16#include "ui/base/models/menu_model.h"
17#include "webkit/glue/window_open_disposition.h"
18
19class Browser;
20class SkBitmap;
21class TabContents;
22class NavigationEntry;
23
24///////////////////////////////////////////////////////////////////////////////
25//
26// BackForwardMenuModel
27//
28// Interface for the showing of the dropdown menu for the Back/Forward buttons.
29// Actual implementations are platform-specific.
30///////////////////////////////////////////////////////////////////////////////
31class BackForwardMenuModel : public ui::MenuModel {
32 public:
33  // These are IDs used to identify individual UI elements within the
34  // browser window using View::GetViewByID.
35  enum ModelType {
36    FORWARD_MENU = 1,
37    BACKWARD_MENU = 2
38  };
39
40  BackForwardMenuModel(Browser* browser, ModelType model_type);
41  virtual ~BackForwardMenuModel();
42
43  // MenuModel implementation.
44  virtual bool HasIcons() const;
45  // Returns how many items the menu should show, including history items,
46  // chapter-stops, separators and the Show Full History link. This function
47  // uses GetHistoryItemCount() and GetChapterStopCount() internally to figure
48  // out the total number of items to show.
49  virtual int GetItemCount() const;
50  virtual ItemType GetTypeAt(int index) const;
51  virtual int GetCommandIdAt(int index) const;
52  virtual string16 GetLabelAt(int index) const;
53  virtual bool IsItemDynamicAt(int index) const;
54  virtual bool GetAcceleratorAt(int index,
55                                ui::Accelerator* accelerator) const;
56  virtual bool IsItemCheckedAt(int index) const;
57  virtual int GetGroupIdAt(int index) const;
58  virtual bool GetIconAt(int index, SkBitmap* icon);
59  virtual ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const;
60  virtual bool IsEnabledAt(int index) const;
61  virtual MenuModel* GetSubmenuModelAt(int index) const;
62  virtual void HighlightChangedTo(int index);
63  virtual void ActivatedAt(int index);
64  virtual void ActivatedAtWithDisposition(int index, int disposition);
65  virtual void MenuWillShow();
66
67  // Is the item at |index| a separator?
68  bool IsSeparator(int index) const;
69
70  // Set the delegate for triggering OnIconChanged.
71  virtual void SetMenuModelDelegate(ui::MenuModelDelegate* menu_model_delegate);
72
73 protected:
74   ui::MenuModelDelegate* menu_model_delegate() { return menu_model_delegate_; }
75
76 private:
77  friend class BackFwdMenuModelTest;
78  FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, BasicCase);
79  FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, MaxItemsTest);
80  FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, ChapterStops);
81  FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, EscapeLabel);
82  FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, FaviconLoadTest);
83
84  // Requests a favicon from the FaviconService. Called by GetIconAt if the
85  // NavigationEntry has an invalid favicon.
86  void FetchFavicon(NavigationEntry* entry);
87
88  // Callback from the favicon service.
89  void OnFavIconDataAvailable(FaviconService::Handle handle,
90                              history::FaviconData favicon);
91
92  // Allows the unit test to use its own dummy tab contents.
93  void set_test_tab_contents(TabContents* test_tab_contents) {
94    test_tab_contents_ = test_tab_contents;
95  }
96
97  // Returns how many history items the menu should show. For example, if the
98  // navigation controller of the current tab has a current entry index of 5 and
99  // forward_direction_ is false (we are the back button delegate) then this
100  // function will return 5 (representing 0-4). If forward_direction_ is
101  // true (we are the forward button delegate), then this function will return
102  // the number of entries after 5. Note, though, that in either case it will
103  // not report more than kMaxHistoryItems. The number returned also does not
104  // include the separator line after the history items (nor the separator for
105  // the "Show Full History" link).
106  int GetHistoryItemCount() const;
107
108  // Returns how many chapter-stop items the menu should show. For the
109  // definition of a chapter-stop, see GetIndexOfNextChapterStop(). The number
110  // returned does not include the separator lines before and after the
111  // chapter-stops.
112  int GetChapterStopCount(int history_items) const;
113
114  // Finds the next chapter-stop in the NavigationEntryList starting from
115  // the index specified in |start_from| and continuing in the direction
116  // specified (|forward|) until either a chapter-stop is found or we reach the
117  // end, in which case -1 is returned. If |start_from| is out of bounds, -1
118  // will also be returned. A chapter-stop is defined as the last page the user
119  // browsed to within the same domain. For example, if the user's homepage is
120  // Google and she navigates to Google pages G1, G2 and G3 before heading over
121  // to WikiPedia for pages W1 and W2 and then back to Google for pages G4 and
122  // G5 then G3, W2 and G5 are considered chapter-stops. The return value from
123  // this function is an index into the NavigationEntryList vector.
124  int GetIndexOfNextChapterStop(int start_from, bool forward) const;
125
126  // Finds a given chapter-stop starting at the currently active entry in the
127  // NavigationEntryList vector advancing first forward or backward by |offset|
128  // (depending on the direction specified in parameter |forward|). It also
129  // allows you to skip chapter-stops by specifying a positive value for |skip|.
130  // Example: FindChapterStop(5, false, 3) starts with the currently active
131  // index, subtracts 5 from it and then finds the fourth chapter-stop before
132  // that index (skipping the first 3 it finds).
133  // Example: FindChapterStop(0, true, 0) is functionally equivalent to
134  // calling GetIndexOfNextChapterStop(GetCurrentEntryIndex(), true).
135  //
136  // NOTE: Both |offset| and |skip| must be non-negative. The return value from
137  // this function is an index into the NavigationEntryList vector. If |offset|
138  // is out of bounds or if we skip too far (run out of chapter-stops) this
139  // function returns -1.
140  int FindChapterStop(int offset, bool forward, int skip) const;
141
142  // How many items (max) to show in the back/forward history menu dropdown.
143  static const int kMaxHistoryItems;
144
145  // How many chapter-stops (max) to show in the back/forward dropdown list.
146  static const int kMaxChapterStops;
147
148  // Takes a menu item index as passed in through one of the menu delegate
149  // functions and converts it into an index into the NavigationEntryList
150  // vector. |index| can point to a separator, or the
151  // "Show Full History" link in which case this function returns -1.
152  int MenuIndexToNavEntryIndex(int index) const;
153
154  // Does the item have a command associated with it?
155  bool ItemHasCommand(int index) const;
156
157  // Returns true if there is an icon for this menu item.
158  bool ItemHasIcon(int index) const;
159
160  // Allow the unit test to use the "Show Full History" label.
161  string16 GetShowFullHistoryLabel() const;
162
163  // Looks up a NavigationEntry by menu id.
164  NavigationEntry* GetNavigationEntry(int index) const;
165
166  // Retrieves the TabContents pointer to use, which is either the one that
167  // the unit test sets (using SetTabContentsForUnitTest) or the one from
168  // the browser window.
169  TabContents* GetTabContents() const;
170
171  // Build a string version of a user action on this menu, used as an
172  // identifier for logging user behavior.
173  // E.g. BuildActionName("Click", 2) returns "BackMenu_Click2".
174  // An index of -1 means no index.
175  std::string BuildActionName(const std::string& name, int index) const;
176
177  Browser* browser_;
178
179  // The unit tests will provide their own TabContents to use.
180  TabContents* test_tab_contents_;
181
182  // Represents whether this is the delegate for the forward button or the
183  // back button.
184  ModelType model_type_;
185
186  // Keeps track of which favicons have already been requested from the history
187  // to prevent duplicate requests, identified by NavigationEntry->unique_id().
188  std::set<int> requested_favicons_;
189
190  // Used for loading favicons from history.
191  CancelableRequestConsumerTSimple<int> load_consumer_;
192
193  // Used for receiving notifications when an icon is changed.
194  ui::MenuModelDelegate* menu_model_delegate_;
195
196  DISALLOW_COPY_AND_ASSIGN(BackForwardMenuModel);
197};
198
199#endif  // CHROME_BROWSER_UI_TOOLBAR_BACK_FORWARD_MENU_MODEL_H_
200