back_forward_menu_model.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
13345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "build/build_config.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/toolbar/back_forward_menu_model.h"
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/l10n_util.h"
10201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "app/text_elider.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/resource_bundle.h"
123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/metrics/user_metrics.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/tab_contents/navigation_controller.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/tab_contents/navigation_entry.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/tab_contents/tab_contents.h"
173f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/browser/prefs/pref_service.h"
183f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/browser/profiles/profile.h"
194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser.h"
203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "chrome/common/pref_names.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/url_constants.h"
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h"
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/registry_controlled_domain.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int BackForwardMenuModel::kMaxHistoryItems = 12;
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int BackForwardMenuModel::kMaxChapterStops = 5;
28201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochstatic const int kMaxWidth = 700;
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBackForwardMenuModel::BackForwardMenuModel(Browser* browser,
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                           ModelType model_type)
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : browser_(browser),
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      test_tab_contents_(NULL),
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_type_(model_type) {
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::HasIcons() const {
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::GetItemCount() const {
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int items = GetHistoryItemCount();
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (items > 0) {
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int chapter_stops = 0;
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Next, we count ChapterStops, if any.
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (items == kMaxHistoryItems)
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chapter_stops = GetChapterStopCount(items);
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (chapter_stops)
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      items += chapter_stops + 1;  // Chapter stops also need a separator.
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If the menu is not empty, add two positions in the end
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // for a separator and a "Show Full History" item.
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    items += 2;
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return items;
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochmenus::MenuModel::ItemType BackForwardMenuModel::GetTypeAt(int index) const {
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return IsSeparator(index) ? TYPE_SEPARATOR : TYPE_COMMAND;
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::GetCommandIdAt(int index) const {
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return index;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring16 BackForwardMenuModel::GetLabelAt(int index) const {
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Return label "Show Full History" for the last item of the menu.
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index == GetItemCount() - 1)
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return l10n_util::GetStringUTF16(IDS_SHOWFULLHISTORY_LINK);
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Return an empty string for a separator.
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (IsSeparator(index))
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return string16();
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
79201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  // Return the entry title, escaping any '&' characters and eliding it if it's
80201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  // super long.
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NavigationEntry* entry = GetNavigationEntry(index);
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  string16 menu_text(entry->GetTitleForDisplay(
833f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      GetTabContents()->profile()->GetPrefs()->
843f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          GetString(prefs::kAcceptLanguages)));
85201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  menu_text = gfx::ElideText(menu_text, gfx::Font(), kMaxWidth, false);
86201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = menu_text.find('&'); i != string16::npos;
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i = menu_text.find('&', i + 2)) {
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    menu_text.insert(i, 1, '&');
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return menu_text;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool BackForwardMenuModel::IsItemDynamicAt(int index) const {
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This object is only used for a single showing of a menu.
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::GetAcceleratorAt(
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int index,
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    menus::Accelerator* accelerator) const {
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::IsItemCheckedAt(int index) const {
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::GetGroupIdAt(int index) const {
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::GetIconAt(int index, SkBitmap* icon) const {
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!ItemHasIcon(index))
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index == GetItemCount() - 1) {
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *icon = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        IDR_HISTORY_FAVICON);
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NavigationEntry* entry = GetNavigationEntry(index);
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *icon = entry->favicon().bitmap();
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochmenus::ButtonMenuItemModel* BackForwardMenuModel::GetButtonMenuItemAt(
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int index) const {
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return NULL;
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::IsEnabledAt(int index) const {
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return index < GetItemCount() && !IsSeparator(index);
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochmenus::MenuModel* BackForwardMenuModel::GetSubmenuModelAt(int index) const {
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return NULL;
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BackForwardMenuModel::HighlightChangedTo(int index) {
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BackForwardMenuModel::ActivatedAt(int index) {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ActivatedAtWithDisposition(index, CURRENT_TAB);
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BackForwardMenuModel::ActivatedAtWithDisposition(
1494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      int index, int disposition) {
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Profile* profile = browser_->profile();
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(!IsSeparator(index));
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Execute the command for the last item: "Show Full History".
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index == GetItemCount() - 1) {
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UserMetrics::RecordComputedAction(BuildActionName("ShowFullHistory", -1),
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      profile);
1584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    browser_->ShowSingletonTab(GURL(chrome::kChromeUIHistoryURL), false);
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Log whether it was a history or chapter click.
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index < GetHistoryItemCount()) {
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UserMetrics::RecordComputedAction(
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        BuildActionName("HistoryClick", index), profile);
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UserMetrics::RecordComputedAction(
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        BuildActionName("ChapterClick", index - GetHistoryItemCount() - 1),
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        profile);
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int controller_index = MenuIndexToNavEntryIndex(index);
1734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!browser_->NavigateToIndexWithDisposition(
1744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          controller_index, static_cast<WindowOpenDisposition>(disposition))) {
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BackForwardMenuModel::MenuWillShow() {
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UserMetrics::RecordComputedAction(BuildActionName("Popup", -1),
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    browser_->profile());
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::IsSeparator(int index) const {
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int history_items = GetHistoryItemCount();
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the index is past the number of history items + separator,
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we then consider if it is a chapter-stop entry.
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index > history_items) {
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We either are in ChapterStop area, or at the end of the list (the "Show
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Full History" link).
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int chapter_stops = GetChapterStopCount(history_items);
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (chapter_stops == 0)
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;  // We must have reached the "Show Full History" link.
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Otherwise, look to see if we have reached the separator for the
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // chapter-stops. If not, this is a chapter stop.
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return (index == history_items + 1 + chapter_stops);
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Look to see if we have reached the separator for the history items.
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return index == history_items;
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::GetHistoryItemCount() const {
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabContents* contents = GetTabContents();
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int items = 0;
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_type_ == FORWARD_MENU) {
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Only count items from n+1 to end (if n is current entry)
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    items = contents->controller().entry_count() -
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            contents->controller().GetCurrentEntryIndex() - 1;
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    items = contents->controller().GetCurrentEntryIndex();
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (items > kMaxHistoryItems)
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    items = kMaxHistoryItems;
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else if (items < 0)
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    items = 0;
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return items;
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::GetChapterStopCount(int history_items) const {
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabContents* contents = GetTabContents();
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int chapter_stops = 0;
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int current_entry = contents->controller().GetCurrentEntryIndex();
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (history_items == kMaxHistoryItems) {
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int chapter_id = current_entry;
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (model_type_ == FORWARD_MENU) {
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chapter_id += history_items;
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chapter_id -= history_items;
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    do {
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chapter_id = GetIndexOfNextChapterStop(chapter_id,
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          model_type_ == FORWARD_MENU);
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (chapter_id != -1)
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        ++chapter_stops;
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } while (chapter_id != -1 && chapter_stops < kMaxChapterStops);
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return chapter_stops;
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::GetIndexOfNextChapterStop(int start_from,
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                    bool forward) const {
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabContents* contents = GetTabContents();
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NavigationController& controller = contents->controller();
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int max_count = controller.entry_count();
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (start_from < 0 || start_from >= max_count)
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;  // Out of bounds.
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (forward) {
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (start_from < max_count - 1) {
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // We want to advance over the current chapter stop, so we add one.
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // We don't need to do this when direction is backwards.
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      start_from++;
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return -1;
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NavigationEntry* start_entry = controller.GetEntryAtIndex(start_from);
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const GURL& url = start_entry->url();
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!forward) {
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // When going backwards we return the first entry we find that has a
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // different domain.
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = start_from - 1; i >= 0; --i) {
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!net::RegistryControlledDomainService::SameDomainOrHost(url,
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              controller.GetEntryAtIndex(i)->url()))
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return i;
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We have reached the beginning without finding a chapter stop.
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // When going forwards we return the entry before the entry that has a
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // different domain.
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = start_from + 1; i < max_count; ++i) {
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!net::RegistryControlledDomainService::SameDomainOrHost(url,
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              controller.GetEntryAtIndex(i)->url()))
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return i - 1;
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Last entry is always considered a chapter stop.
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return max_count - 1;
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::FindChapterStop(int offset,
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          bool forward,
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          int skip) const {
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (offset < 0 || skip < 0)
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!forward)
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    offset *= -1;
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabContents* contents = GetTabContents();
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int entry = contents->controller().GetCurrentEntryIndex() + offset;
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < skip + 1; i++)
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry = GetIndexOfNextChapterStop(entry, forward);
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return entry;
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::ItemHasCommand(int index) const {
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return index < GetItemCount() && !IsSeparator(index);
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BackForwardMenuModel::ItemHasIcon(int index) const {
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return index < GetItemCount() && !IsSeparator(index);
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring16 BackForwardMenuModel::GetShowFullHistoryLabel() const {
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return l10n_util::GetStringUTF16(IDS_SHOWFULLHISTORY_LINK);
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabContents* BackForwardMenuModel::GetTabContents() const {
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We use the test tab contents if the unit test has specified it.
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return test_tab_contents_ ? test_tab_contents_ :
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              browser_->GetSelectedTabContents();
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint BackForwardMenuModel::MenuIndexToNavEntryIndex(int index) const {
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabContents* contents = GetTabContents();
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int history_items = GetHistoryItemCount();
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_GE(index, 0);
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Convert anything above the History items separator.
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index < history_items) {
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (model_type_ == FORWARD_MENU) {
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      index += contents->controller().GetCurrentEntryIndex() + 1;
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Back menu is reverse.
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      index = contents->controller().GetCurrentEntryIndex() - (index + 1);
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return index;
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index == history_items)
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;  // Don't translate the separator for history items.
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index >= history_items + 1 + GetChapterStopCount(history_items))
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;  // This is beyond the last chapter stop so we abort.
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This menu item is a chapter stop located between the two separators.
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  index = FindChapterStop(history_items,
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          model_type_ == FORWARD_MENU,
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          index - history_items - 1);
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return index;
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochNavigationEntry* BackForwardMenuModel::GetNavigationEntry(int index) const {
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int controller_index = MenuIndexToNavEntryIndex(index);
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NavigationController& controller = GetTabContents()->controller();
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (controller_index >= 0 && controller_index < controller.entry_count())
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return controller.GetEntryAtIndex(controller_index);
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NOTREACHED();
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return NULL;
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::string BackForwardMenuModel::BuildActionName(
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& action, int index) const {
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(!action.empty());
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(index >= -1);
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string metric_string;
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_type_ == FORWARD_MENU)
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    metric_string += "ForwardMenu_";
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    metric_string += "BackMenu_";
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  metric_string += action;
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index != -1) {
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // +1 is for historical reasons (indices used to start at 1).
3803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    metric_string += base::IntToString(index + 1);
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return metric_string;
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
384