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