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