summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/toolbar
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/ui/toolbar')
-rw-r--r--chrome/browser/ui/toolbar/back_forward_menu_model.cc379
-rw-r--r--chrome/browser/ui/toolbar/back_forward_menu_model.h171
-rw-r--r--chrome/browser/ui/toolbar/back_forward_menu_model_unittest.cc422
-rw-r--r--chrome/browser/ui/toolbar/encoding_menu_controller.cc141
-rw-r--r--chrome/browser/ui/toolbar/encoding_menu_controller.h56
-rw-r--r--chrome/browser/ui/toolbar/encoding_menu_controller_unittest.cc93
-rw-r--r--chrome/browser/ui/toolbar/toolbar_model.cc129
-rw-r--r--chrome/browser/ui/toolbar/toolbar_model.h71
-rw-r--r--chrome/browser/ui/toolbar/wrench_menu_model.cc536
-rw-r--r--chrome/browser/ui/toolbar/wrench_menu_model.h150
-rw-r--r--chrome/browser/ui/toolbar/wrench_menu_model_unittest.cc105
11 files changed, 2253 insertions, 0 deletions
diff --git a/chrome/browser/ui/toolbar/back_forward_menu_model.cc b/chrome/browser/ui/toolbar/back_forward_menu_model.cc
new file mode 100644
index 0000000..390dce0
--- /dev/null
+++ b/chrome/browser/ui/toolbar/back_forward_menu_model.cc
@@ -0,0 +1,379 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#include "chrome/browser/ui/toolbar/back_forward_menu_model.h"
+
+#include "app/l10n_util.h"
+#include "app/text_elider.h"
+#include "app/resource_bundle.h"
+#include "base/string_number_conversions.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/url_constants.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "net/base/registry_controlled_domain.h"
+
+const int BackForwardMenuModel::kMaxHistoryItems = 12;
+const int BackForwardMenuModel::kMaxChapterStops = 5;
+static const int kMaxWidth = 700;
+
+BackForwardMenuModel::BackForwardMenuModel(Browser* browser,
+ ModelType model_type)
+ : browser_(browser),
+ test_tab_contents_(NULL),
+ model_type_(model_type) {
+}
+
+bool BackForwardMenuModel::HasIcons() const {
+ return true;
+}
+
+int BackForwardMenuModel::GetItemCount() const {
+ int items = GetHistoryItemCount();
+
+ if (items > 0) {
+ int chapter_stops = 0;
+
+ // Next, we count ChapterStops, if any.
+ if (items == kMaxHistoryItems)
+ chapter_stops = GetChapterStopCount(items);
+
+ if (chapter_stops)
+ items += chapter_stops + 1; // Chapter stops also need a separator.
+
+ // If the menu is not empty, add two positions in the end
+ // for a separator and a "Show Full History" item.
+ items += 2;
+ }
+
+ return items;
+}
+
+menus::MenuModel::ItemType BackForwardMenuModel::GetTypeAt(int index) const {
+ return IsSeparator(index) ? TYPE_SEPARATOR : TYPE_COMMAND;
+}
+
+int BackForwardMenuModel::GetCommandIdAt(int index) const {
+ return index;
+}
+
+string16 BackForwardMenuModel::GetLabelAt(int index) const {
+ // Return label "Show Full History" for the last item of the menu.
+ if (index == GetItemCount() - 1)
+ return l10n_util::GetStringUTF16(IDS_SHOWFULLHISTORY_LINK);
+
+ // Return an empty string for a separator.
+ if (IsSeparator(index))
+ return string16();
+
+ // Return the entry title, escaping any '&' characters and eliding it if it's
+ // super long.
+ NavigationEntry* entry = GetNavigationEntry(index);
+ string16 menu_text(entry->GetTitleForDisplay(
+ &GetTabContents()->controller()));
+ menu_text = gfx::ElideText(menu_text, gfx::Font(), kMaxWidth, false);
+
+ for (size_t i = menu_text.find('&'); i != string16::npos;
+ i = menu_text.find('&', i + 2)) {
+ menu_text.insert(i, 1, '&');
+ }
+ return menu_text;
+}
+
+bool BackForwardMenuModel::IsItemDynamicAt(int index) const {
+ // This object is only used for a single showing of a menu.
+ return false;
+}
+
+bool BackForwardMenuModel::GetAcceleratorAt(
+ int index,
+ menus::Accelerator* accelerator) const {
+ return false;
+}
+
+bool BackForwardMenuModel::IsItemCheckedAt(int index) const {
+ return false;
+}
+
+int BackForwardMenuModel::GetGroupIdAt(int index) const {
+ return false;
+}
+
+bool BackForwardMenuModel::GetIconAt(int index, SkBitmap* icon) const {
+ if (!ItemHasIcon(index))
+ return false;
+
+ if (index == GetItemCount() - 1) {
+ *icon = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_HISTORY_FAVICON);
+ } else {
+ NavigationEntry* entry = GetNavigationEntry(index);
+ *icon = entry->favicon().bitmap();
+ }
+
+ return true;
+}
+
+menus::ButtonMenuItemModel* BackForwardMenuModel::GetButtonMenuItemAt(
+ int index) const {
+ return NULL;
+}
+
+bool BackForwardMenuModel::IsEnabledAt(int index) const {
+ return index < GetItemCount() && !IsSeparator(index);
+}
+
+menus::MenuModel* BackForwardMenuModel::GetSubmenuModelAt(int index) const {
+ return NULL;
+}
+
+void BackForwardMenuModel::HighlightChangedTo(int index) {
+}
+
+void BackForwardMenuModel::ActivatedAt(int index) {
+ ActivatedAtWithDisposition(index, CURRENT_TAB);
+}
+
+void BackForwardMenuModel::ActivatedAtWithDisposition(
+ int index, int disposition) {
+ Profile* profile = browser_->profile();
+
+ DCHECK(!IsSeparator(index));
+
+ // Execute the command for the last item: "Show Full History".
+ if (index == GetItemCount() - 1) {
+ UserMetrics::RecordComputedAction(BuildActionName("ShowFullHistory", -1),
+ profile);
+ browser_->ShowSingletonTab(GURL(chrome::kChromeUIHistoryURL), false);
+ return;
+ }
+
+ // Log whether it was a history or chapter click.
+ if (index < GetHistoryItemCount()) {
+ UserMetrics::RecordComputedAction(
+ BuildActionName("HistoryClick", index), profile);
+ } else {
+ UserMetrics::RecordComputedAction(
+ BuildActionName("ChapterClick", index - GetHistoryItemCount() - 1),
+ profile);
+ }
+
+ int controller_index = MenuIndexToNavEntryIndex(index);
+ if (!browser_->NavigateToIndexWithDisposition(
+ controller_index, static_cast<WindowOpenDisposition>(disposition))) {
+ NOTREACHED();
+ }
+}
+
+void BackForwardMenuModel::MenuWillShow() {
+ UserMetrics::RecordComputedAction(BuildActionName("Popup", -1),
+ browser_->profile());
+}
+
+bool BackForwardMenuModel::IsSeparator(int index) const {
+ int history_items = GetHistoryItemCount();
+ // If the index is past the number of history items + separator,
+ // we then consider if it is a chapter-stop entry.
+ if (index > history_items) {
+ // We either are in ChapterStop area, or at the end of the list (the "Show
+ // Full History" link).
+ int chapter_stops = GetChapterStopCount(history_items);
+ if (chapter_stops == 0)
+ return false; // We must have reached the "Show Full History" link.
+ // Otherwise, look to see if we have reached the separator for the
+ // chapter-stops. If not, this is a chapter stop.
+ return (index == history_items + 1 + chapter_stops);
+ }
+
+ // Look to see if we have reached the separator for the history items.
+ return index == history_items;
+}
+
+int BackForwardMenuModel::GetHistoryItemCount() const {
+ TabContents* contents = GetTabContents();
+ int items = 0;
+
+ if (model_type_ == FORWARD_MENU) {
+ // Only count items from n+1 to end (if n is current entry)
+ items = contents->controller().entry_count() -
+ contents->controller().GetCurrentEntryIndex() - 1;
+ } else {
+ items = contents->controller().GetCurrentEntryIndex();
+ }
+
+ if (items > kMaxHistoryItems)
+ items = kMaxHistoryItems;
+ else if (items < 0)
+ items = 0;
+
+ return items;
+}
+
+int BackForwardMenuModel::GetChapterStopCount(int history_items) const {
+ TabContents* contents = GetTabContents();
+
+ int chapter_stops = 0;
+ int current_entry = contents->controller().GetCurrentEntryIndex();
+
+ if (history_items == kMaxHistoryItems) {
+ int chapter_id = current_entry;
+ if (model_type_ == FORWARD_MENU) {
+ chapter_id += history_items;
+ } else {
+ chapter_id -= history_items;
+ }
+
+ do {
+ chapter_id = GetIndexOfNextChapterStop(chapter_id,
+ model_type_ == FORWARD_MENU);
+ if (chapter_id != -1)
+ ++chapter_stops;
+ } while (chapter_id != -1 && chapter_stops < kMaxChapterStops);
+ }
+
+ return chapter_stops;
+}
+
+int BackForwardMenuModel::GetIndexOfNextChapterStop(int start_from,
+ bool forward) const {
+ TabContents* contents = GetTabContents();
+ NavigationController& controller = contents->controller();
+
+ int max_count = controller.entry_count();
+ if (start_from < 0 || start_from >= max_count)
+ return -1; // Out of bounds.
+
+ if (forward) {
+ if (start_from < max_count - 1) {
+ // We want to advance over the current chapter stop, so we add one.
+ // We don't need to do this when direction is backwards.
+ start_from++;
+ } else {
+ return -1;
+ }
+ }
+
+ NavigationEntry* start_entry = controller.GetEntryAtIndex(start_from);
+ const GURL& url = start_entry->url();
+
+ if (!forward) {
+ // When going backwards we return the first entry we find that has a
+ // different domain.
+ for (int i = start_from - 1; i >= 0; --i) {
+ if (!net::RegistryControlledDomainService::SameDomainOrHost(url,
+ controller.GetEntryAtIndex(i)->url()))
+ return i;
+ }
+ // We have reached the beginning without finding a chapter stop.
+ return -1;
+ } else {
+ // When going forwards we return the entry before the entry that has a
+ // different domain.
+ for (int i = start_from + 1; i < max_count; ++i) {
+ if (!net::RegistryControlledDomainService::SameDomainOrHost(url,
+ controller.GetEntryAtIndex(i)->url()))
+ return i - 1;
+ }
+ // Last entry is always considered a chapter stop.
+ return max_count - 1;
+ }
+}
+
+int BackForwardMenuModel::FindChapterStop(int offset,
+ bool forward,
+ int skip) const {
+ if (offset < 0 || skip < 0)
+ return -1;
+
+ if (!forward)
+ offset *= -1;
+
+ TabContents* contents = GetTabContents();
+ int entry = contents->controller().GetCurrentEntryIndex() + offset;
+ for (int i = 0; i < skip + 1; i++)
+ entry = GetIndexOfNextChapterStop(entry, forward);
+
+ return entry;
+}
+
+bool BackForwardMenuModel::ItemHasCommand(int index) const {
+ return index < GetItemCount() && !IsSeparator(index);
+}
+
+bool BackForwardMenuModel::ItemHasIcon(int index) const {
+ return index < GetItemCount() && !IsSeparator(index);
+}
+
+string16 BackForwardMenuModel::GetShowFullHistoryLabel() const {
+ return l10n_util::GetStringUTF16(IDS_SHOWFULLHISTORY_LINK);
+}
+
+TabContents* BackForwardMenuModel::GetTabContents() const {
+ // We use the test tab contents if the unit test has specified it.
+ return test_tab_contents_ ? test_tab_contents_ :
+ browser_->GetSelectedTabContents();
+}
+
+int BackForwardMenuModel::MenuIndexToNavEntryIndex(int index) const {
+ TabContents* contents = GetTabContents();
+ int history_items = GetHistoryItemCount();
+
+ DCHECK_GE(index, 0);
+
+ // Convert anything above the History items separator.
+ if (index < history_items) {
+ if (model_type_ == FORWARD_MENU) {
+ index += contents->controller().GetCurrentEntryIndex() + 1;
+ } else {
+ // Back menu is reverse.
+ index = contents->controller().GetCurrentEntryIndex() - (index + 1);
+ }
+ return index;
+ }
+ if (index == history_items)
+ return -1; // Don't translate the separator for history items.
+
+ if (index >= history_items + 1 + GetChapterStopCount(history_items))
+ return -1; // This is beyond the last chapter stop so we abort.
+
+ // This menu item is a chapter stop located between the two separators.
+ index = FindChapterStop(history_items,
+ model_type_ == FORWARD_MENU,
+ index - history_items - 1);
+
+ return index;
+}
+
+NavigationEntry* BackForwardMenuModel::GetNavigationEntry(int index) const {
+ int controller_index = MenuIndexToNavEntryIndex(index);
+ NavigationController& controller = GetTabContents()->controller();
+ if (controller_index >= 0 && controller_index < controller.entry_count())
+ return controller.GetEntryAtIndex(controller_index);
+
+ NOTREACHED();
+ return NULL;
+}
+
+std::string BackForwardMenuModel::BuildActionName(
+ const std::string& action, int index) const {
+ DCHECK(!action.empty());
+ DCHECK(index >= -1);
+ std::string metric_string;
+ if (model_type_ == FORWARD_MENU)
+ metric_string += "ForwardMenu_";
+ else
+ metric_string += "BackMenu_";
+ metric_string += action;
+ if (index != -1) {
+ // +1 is for historical reasons (indices used to start at 1).
+ metric_string += base::IntToString(index + 1);
+ }
+ return metric_string;
+}
diff --git a/chrome/browser/ui/toolbar/back_forward_menu_model.h b/chrome/browser/ui/toolbar/back_forward_menu_model.h
new file mode 100644
index 0000000..1d669a0
--- /dev/null
+++ b/chrome/browser/ui/toolbar/back_forward_menu_model.h
@@ -0,0 +1,171 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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_
+#pragma once
+
+#include <string>
+
+#include "app/menus/menu_model.h"
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+#include "webkit/glue/window_open_disposition.h"
+
+class Browser;
+class SkBitmap;
+class TabContents;
+class NavigationEntry;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BackForwardMenuModel
+//
+// Interface for the showing of the dropdown menu for the Back/Forward buttons.
+// Actual implementations are platform-specific.
+///////////////////////////////////////////////////////////////////////////////
+class BackForwardMenuModel : public menus::MenuModel {
+ public:
+ // These are IDs used to identify individual UI elements within the
+ // browser window using View::GetViewByID.
+ enum ModelType {
+ FORWARD_MENU = 1,
+ BACKWARD_MENU = 2
+ };
+
+ BackForwardMenuModel(Browser* browser, ModelType model_type);
+ virtual ~BackForwardMenuModel() { }
+
+ // MenuModel implementation.
+ virtual bool HasIcons() const;
+ // Returns how many items the menu should show, including history items,
+ // chapter-stops, separators and the Show Full History link. This function
+ // uses GetHistoryItemCount() and GetChapterStopCount() internally to figure
+ // out the total number of items to show.
+ virtual int GetItemCount() const;
+ virtual ItemType GetTypeAt(int index) const;
+ virtual int GetCommandIdAt(int index) const;
+ virtual string16 GetLabelAt(int index) const;
+ virtual bool IsItemDynamicAt(int index) const;
+ virtual bool GetAcceleratorAt(int index,
+ menus::Accelerator* accelerator) const;
+ virtual bool IsItemCheckedAt(int index) const;
+ virtual int GetGroupIdAt(int index) const;
+ virtual bool GetIconAt(int index, SkBitmap* icon) const;
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const;
+ virtual bool IsEnabledAt(int index) const;
+ virtual MenuModel* GetSubmenuModelAt(int index) const;
+ virtual void HighlightChangedTo(int index);
+ virtual void ActivatedAt(int index);
+ virtual void ActivatedAtWithDisposition(int index, int disposition);
+ virtual void MenuWillShow();
+
+ // Is the item at |index| a separator?
+ bool IsSeparator(int index) const;
+
+ private:
+ // Allows the unit test to use its own dummy tab contents.
+ void set_test_tab_contents(TabContents* test_tab_contents) {
+ test_tab_contents_ = test_tab_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).
+ int 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.
+ int GetChapterStopCount(int 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 -1 is returned. If |start_from| is out of bounds, -1
+ // 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 she navigates 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.
+ int GetIndexOfNextChapterStop(int 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 -1.
+ int FindChapterStop(int offset, bool forward, int skip) const;
+
+ // How many items (max) to show in the back/forward history menu dropdown.
+ static const int kMaxHistoryItems;
+
+ // How many chapter-stops (max) to show in the back/forward dropdown list.
+ static const int 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 -1.
+ int MenuIndexToNavEntryIndex(int index) const;
+
+ // Does the item have a command associated with it?
+ bool ItemHasCommand(int index) const;
+
+ // Returns true if there is an icon for this menu item.
+ bool ItemHasIcon(int index) const;
+
+ // Allow the unit test to use the "Show Full History" label.
+ string16 GetShowFullHistoryLabel() const;
+
+ // Looks up a NavigationEntry by menu id.
+ NavigationEntry* GetNavigationEntry(int index) const;
+
+ // Retrieves the TabContents pointer to use, which is either the one that
+ // the unit test sets (using SetTabContentsForUnitTest) or the one from
+ // the browser window.
+ TabContents* GetTabContents() 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 -1 means no index.
+ std::string BuildActionName(const std::string& name, int index) const;
+
+ Browser* browser_;
+
+ // The unit tests will provide their own TabContents to use.
+ TabContents* test_tab_contents_;
+
+ // Represents whether this is the delegate for the forward button or the
+ // back button.
+ ModelType model_type_;
+
+ friend class BackFwdMenuModelTest;
+ FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, BasicCase);
+ FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, MaxItemsTest);
+ FRIEND_TEST_ALL_PREFIXES(BackFwdMenuModelTest, ChapterStops);
+
+ DISALLOW_COPY_AND_ASSIGN(BackForwardMenuModel);
+};
+
+#endif // CHROME_BROWSER_UI_TOOLBAR_BACK_FORWARD_MENU_MODEL_H_
diff --git a/chrome/browser/ui/toolbar/back_forward_menu_model_unittest.cc b/chrome/browser/ui/toolbar/back_forward_menu_model_unittest.cc
new file mode 100644
index 0000000..2e4b988
--- /dev/null
+++ b/chrome/browser/ui/toolbar/back_forward_menu_model_unittest.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/back_forward_menu_model.h"
+
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/renderer_host/test/test_render_view_host.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/test_tab_contents.h"
+#include "chrome/common/url_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class BackFwdMenuModelTest : public RenderViewHostTestHarness {
+ public:
+ void ValidateModel(BackForwardMenuModel* model, int history_items,
+ int chapter_stops) {
+ int h = std::min(BackForwardMenuModel::kMaxHistoryItems, history_items);
+ int c = std::min(BackForwardMenuModel::kMaxChapterStops, chapter_stops);
+ EXPECT_EQ(h, model->GetHistoryItemCount());
+ EXPECT_EQ(c, model->GetChapterStopCount(h));
+ if (h > 0)
+ h += 2; // Separator and View History link.
+ if (c > 0)
+ ++c;
+ EXPECT_EQ(h + c, model->GetItemCount());
+ }
+
+ void LoadURLAndUpdateState(const char* url, const char* title) {
+ NavigateAndCommit(GURL(url));
+ controller().GetLastCommittedEntry()->set_title(UTF8ToUTF16(title));
+ }
+
+ // Navigate back or forward the given amount and commits the entry (which
+ // will be pending after we ask to navigate there).
+ void NavigateToOffset(int offset) {
+ controller().GoToOffset(offset);
+ contents()->CommitPendingNavigation();
+ }
+
+ // Same as NavigateToOffset but goes to an absolute index.
+ void NavigateToIndex(int index) {
+ controller().GoToIndex(index);
+ contents()->CommitPendingNavigation();
+ }
+
+ // Goes back/forward and commits the load.
+ void GoBack() {
+ controller().GoBack();
+ contents()->CommitPendingNavigation();
+ }
+ void GoForward() {
+ controller().GoForward();
+ contents()->CommitPendingNavigation();
+ }
+};
+
+TEST_F(BackFwdMenuModelTest, BasicCase) {
+ scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::BACKWARD_MENU));
+ back_model->set_test_tab_contents(contents());
+
+ scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::FORWARD_MENU));
+ forward_model->set_test_tab_contents(contents());
+
+ EXPECT_EQ(0, back_model->GetItemCount());
+ EXPECT_EQ(0, forward_model->GetItemCount());
+ EXPECT_FALSE(back_model->ItemHasCommand(1));
+
+ // Seed the controller with a few URLs
+ LoadURLAndUpdateState("http://www.a.com/1", "A1");
+ LoadURLAndUpdateState("http://www.a.com/2", "A2");
+ LoadURLAndUpdateState("http://www.a.com/3", "A3");
+ LoadURLAndUpdateState("http://www.b.com/1", "B1");
+ LoadURLAndUpdateState("http://www.b.com/2", "B2");
+ LoadURLAndUpdateState("http://www.c.com/1", "C1");
+ LoadURLAndUpdateState("http://www.c.com/2", "C2");
+ LoadURLAndUpdateState("http://www.c.com/3", "C3");
+
+ // There're two more items here: a separator and a "Show Full History".
+ EXPECT_EQ(9, back_model->GetItemCount());
+ EXPECT_EQ(0, forward_model->GetItemCount());
+ EXPECT_EQ(ASCIIToUTF16("C2"), back_model->GetLabelAt(0));
+ EXPECT_EQ(ASCIIToUTF16("A1"), back_model->GetLabelAt(6));
+ EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
+ back_model->GetLabelAt(8));
+
+ EXPECT_TRUE(back_model->ItemHasCommand(0));
+ EXPECT_TRUE(back_model->ItemHasCommand(6));
+ EXPECT_TRUE(back_model->IsSeparator(7));
+ EXPECT_TRUE(back_model->ItemHasCommand(8));
+ EXPECT_FALSE(back_model->ItemHasCommand(9));
+ EXPECT_FALSE(back_model->ItemHasCommand(9));
+
+ NavigateToOffset(-7);
+
+ EXPECT_EQ(0, back_model->GetItemCount());
+ EXPECT_EQ(9, forward_model->GetItemCount());
+ EXPECT_EQ(ASCIIToUTF16("A2"), forward_model->GetLabelAt(0));
+ EXPECT_EQ(ASCIIToUTF16("C3"), forward_model->GetLabelAt(6));
+ EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
+ forward_model->GetLabelAt(8));
+
+ EXPECT_TRUE(forward_model->ItemHasCommand(0));
+ EXPECT_TRUE(forward_model->ItemHasCommand(6));
+ EXPECT_TRUE(forward_model->IsSeparator(7));
+ EXPECT_TRUE(forward_model->ItemHasCommand(8));
+ EXPECT_FALSE(forward_model->ItemHasCommand(7));
+ EXPECT_FALSE(forward_model->ItemHasCommand(9));
+
+ NavigateToOffset(4);
+
+ EXPECT_EQ(6, back_model->GetItemCount());
+ EXPECT_EQ(5, forward_model->GetItemCount());
+ EXPECT_EQ(ASCIIToUTF16("B1"), back_model->GetLabelAt(0));
+ EXPECT_EQ(ASCIIToUTF16("A1"), back_model->GetLabelAt(3));
+ EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
+ back_model->GetLabelAt(5));
+ EXPECT_EQ(ASCIIToUTF16("C1"), forward_model->GetLabelAt(0));
+ EXPECT_EQ(ASCIIToUTF16("C3"), forward_model->GetLabelAt(2));
+ EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
+ forward_model->GetLabelAt(4));
+}
+
+TEST_F(BackFwdMenuModelTest, MaxItemsTest) {
+ scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::BACKWARD_MENU));
+ back_model->set_test_tab_contents(contents());
+
+ scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::FORWARD_MENU));
+ forward_model->set_test_tab_contents(contents());
+
+ // Seed the controller with 32 URLs
+ LoadURLAndUpdateState("http://www.a.com/1", "A1");
+ LoadURLAndUpdateState("http://www.a.com/2", "A2");
+ LoadURLAndUpdateState("http://www.a.com/3", "A3");
+ LoadURLAndUpdateState("http://www.b.com/1", "B1");
+ LoadURLAndUpdateState("http://www.b.com/2", "B2");
+ LoadURLAndUpdateState("http://www.b.com/3", "B3");
+ LoadURLAndUpdateState("http://www.c.com/1", "C1");
+ LoadURLAndUpdateState("http://www.c.com/2", "C2");
+ LoadURLAndUpdateState("http://www.c.com/3", "C3");
+ LoadURLAndUpdateState("http://www.d.com/1", "D1");
+ LoadURLAndUpdateState("http://www.d.com/2", "D2");
+ LoadURLAndUpdateState("http://www.d.com/3", "D3");
+ LoadURLAndUpdateState("http://www.e.com/1", "E1");
+ LoadURLAndUpdateState("http://www.e.com/2", "E2");
+ LoadURLAndUpdateState("http://www.e.com/3", "E3");
+ LoadURLAndUpdateState("http://www.f.com/1", "F1");
+ LoadURLAndUpdateState("http://www.f.com/2", "F2");
+ LoadURLAndUpdateState("http://www.f.com/3", "F3");
+ LoadURLAndUpdateState("http://www.g.com/1", "G1");
+ LoadURLAndUpdateState("http://www.g.com/2", "G2");
+ LoadURLAndUpdateState("http://www.g.com/3", "G3");
+ LoadURLAndUpdateState("http://www.h.com/1", "H1");
+ LoadURLAndUpdateState("http://www.h.com/2", "H2");
+ LoadURLAndUpdateState("http://www.h.com/3", "H3");
+ LoadURLAndUpdateState("http://www.i.com/1", "I1");
+ LoadURLAndUpdateState("http://www.i.com/2", "I2");
+ LoadURLAndUpdateState("http://www.i.com/3", "I3");
+ LoadURLAndUpdateState("http://www.j.com/1", "J1");
+ LoadURLAndUpdateState("http://www.j.com/2", "J2");
+ LoadURLAndUpdateState("http://www.j.com/3", "J3");
+ LoadURLAndUpdateState("http://www.k.com/1", "K1");
+ LoadURLAndUpdateState("http://www.k.com/2", "K2");
+
+ // Also there're two more for a separator and a "Show Full History".
+ int chapter_stop_offset = 6;
+ EXPECT_EQ(BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset,
+ back_model->GetItemCount());
+ EXPECT_EQ(0, forward_model->GetItemCount());
+ EXPECT_EQ(ASCIIToUTF16("K1"), back_model->GetLabelAt(0));
+ EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
+ back_model->GetLabelAt(BackForwardMenuModel::kMaxHistoryItems + 1 +
+ chapter_stop_offset));
+
+ // Test for out of bounds (beyond Show Full History).
+ EXPECT_FALSE(back_model->ItemHasCommand(
+ BackForwardMenuModel::kMaxHistoryItems + chapter_stop_offset + 2));
+
+ EXPECT_TRUE(back_model->ItemHasCommand(
+ BackForwardMenuModel::kMaxHistoryItems - 1));
+ EXPECT_TRUE(back_model->IsSeparator(
+ BackForwardMenuModel::kMaxHistoryItems));
+
+ NavigateToIndex(0);
+
+ EXPECT_EQ(BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset,
+ forward_model->GetItemCount());
+ EXPECT_EQ(0, back_model->GetItemCount());
+ EXPECT_EQ(ASCIIToUTF16("A2"), forward_model->GetLabelAt(0));
+ EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
+ forward_model->GetLabelAt(BackForwardMenuModel::kMaxHistoryItems + 1 +
+ chapter_stop_offset));
+
+ // Out of bounds
+ EXPECT_FALSE(forward_model->ItemHasCommand(
+ BackForwardMenuModel::kMaxHistoryItems + 2 + chapter_stop_offset));
+
+ EXPECT_TRUE(forward_model->ItemHasCommand(
+ BackForwardMenuModel::kMaxHistoryItems - 1));
+ EXPECT_TRUE(forward_model->IsSeparator(
+ BackForwardMenuModel::kMaxHistoryItems));
+}
+
+TEST_F(BackFwdMenuModelTest, ChapterStops) {
+ scoped_ptr<BackForwardMenuModel> back_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::BACKWARD_MENU));
+ back_model->set_test_tab_contents(contents());
+
+ scoped_ptr<BackForwardMenuModel> forward_model(new BackForwardMenuModel(
+ NULL, BackForwardMenuModel::FORWARD_MENU));
+ forward_model->set_test_tab_contents(contents());
+
+ // Seed the controller with 32 URLs.
+ int i = 0;
+ LoadURLAndUpdateState("http://www.a.com/1", "A1");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.a.com/2", "A2");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.a.com/3", "A3");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.b.com/1", "B1");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.b.com/2", "B2");
+ ValidateModel(back_model.get(), i++, 0);
+ // i = 5
+ LoadURLAndUpdateState("http://www.b.com/3", "B3");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.c.com/1", "C1");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.c.com/2", "C2");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.c.com/3", "C3");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.d.com/1", "D1");
+ ValidateModel(back_model.get(), i++, 0);
+ // i = 10
+ LoadURLAndUpdateState("http://www.d.com/2", "D2");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.d.com/3", "D3");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.e.com/1", "E1");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.e.com/2", "E2");
+ ValidateModel(back_model.get(), i++, 0);
+ LoadURLAndUpdateState("http://www.e.com/3", "E3");
+ ValidateModel(back_model.get(), i++, 0);
+ // i = 15
+ LoadURLAndUpdateState("http://www.f.com/1", "F1");
+ ValidateModel(back_model.get(), i++, 1);
+ LoadURLAndUpdateState("http://www.f.com/2", "F2");
+ ValidateModel(back_model.get(), i++, 1);
+ LoadURLAndUpdateState("http://www.f.com/3", "F3");
+ ValidateModel(back_model.get(), i++, 1);
+ LoadURLAndUpdateState("http://www.g.com/1", "G1");
+ ValidateModel(back_model.get(), i++, 2);
+ LoadURLAndUpdateState("http://www.g.com/2", "G2");
+ ValidateModel(back_model.get(), i++, 2);
+ // i = 20
+ LoadURLAndUpdateState("http://www.g.com/3", "G3");
+ ValidateModel(back_model.get(), i++, 2);
+ LoadURLAndUpdateState("http://www.h.com/1", "H1");
+ ValidateModel(back_model.get(), i++, 3);
+ LoadURLAndUpdateState("http://www.h.com/2", "H2");
+ ValidateModel(back_model.get(), i++, 3);
+ LoadURLAndUpdateState("http://www.h.com/3", "H3");
+ ValidateModel(back_model.get(), i++, 3);
+ LoadURLAndUpdateState("http://www.i.com/1", "I1");
+ ValidateModel(back_model.get(), i++, 4);
+ // i = 25
+ LoadURLAndUpdateState("http://www.i.com/2", "I2");
+ ValidateModel(back_model.get(), i++, 4);
+ LoadURLAndUpdateState("http://www.i.com/3", "I3");
+ ValidateModel(back_model.get(), i++, 4);
+ LoadURLAndUpdateState("http://www.j.com/1", "J1");
+ ValidateModel(back_model.get(), i++, 5);
+ LoadURLAndUpdateState("http://www.j.com/2", "J2");
+ ValidateModel(back_model.get(), i++, 5);
+ LoadURLAndUpdateState("http://www.j.com/3", "J3");
+ ValidateModel(back_model.get(), i++, 5);
+ // i = 30
+ LoadURLAndUpdateState("http://www.k.com/1", "K1");
+ ValidateModel(back_model.get(), i++, 6);
+ LoadURLAndUpdateState("http://www.k.com/2", "K2");
+ ValidateModel(back_model.get(), i++, 6);
+ // i = 32
+ LoadURLAndUpdateState("http://www.k.com/3", "K3");
+ ValidateModel(back_model.get(), i++, 6);
+
+ // A chapter stop is defined as the last page the user
+ // browsed to within the same domain.
+
+ // Check to see if the chapter stops have the right labels.
+ int index = BackForwardMenuModel::kMaxHistoryItems;
+ // Empty string indicates item is a separator.
+ EXPECT_EQ(ASCIIToUTF16(""), back_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("F3"), back_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("E3"), back_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("D3"), back_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("C3"), back_model->GetLabelAt(index++));
+ // The menu should only show a maximum of 5 chapter stops.
+ EXPECT_EQ(ASCIIToUTF16("B3"), back_model->GetLabelAt(index));
+ // Empty string indicates item is a separator.
+ EXPECT_EQ(ASCIIToUTF16(""), back_model->GetLabelAt(index + 1));
+ EXPECT_EQ(back_model->GetShowFullHistoryLabel(),
+ back_model->GetLabelAt(index + 2));
+
+ // If we go back two we should still see the same chapter stop at the end.
+ GoBack();
+ EXPECT_EQ(ASCIIToUTF16("B3"), back_model->GetLabelAt(index));
+ GoBack();
+ EXPECT_EQ(ASCIIToUTF16("B3"), back_model->GetLabelAt(index));
+ // But if we go back again, it should change.
+ GoBack();
+ EXPECT_EQ(ASCIIToUTF16("A3"), back_model->GetLabelAt(index));
+ GoBack();
+ EXPECT_EQ(ASCIIToUTF16("A3"), back_model->GetLabelAt(index));
+ GoBack();
+ EXPECT_EQ(ASCIIToUTF16("A3"), back_model->GetLabelAt(index));
+ GoBack();
+ // It is now a separator.
+ EXPECT_EQ(ASCIIToUTF16(""), back_model->GetLabelAt(index));
+ // Undo our position change.
+ NavigateToOffset(6);
+
+ // Go back enough to make sure no chapter stops should appear.
+ NavigateToOffset(-BackForwardMenuModel::kMaxHistoryItems);
+ ValidateModel(forward_model.get(), BackForwardMenuModel::kMaxHistoryItems, 0);
+ // Go forward (still no chapter stop)
+ GoForward();
+ ValidateModel(forward_model.get(),
+ BackForwardMenuModel::kMaxHistoryItems - 1, 0);
+ // Go back two (one chapter stop should show up)
+ GoBack();
+ GoBack();
+ ValidateModel(forward_model.get(),
+ BackForwardMenuModel::kMaxHistoryItems, 1);
+
+ // Go to beginning.
+ NavigateToIndex(0);
+
+ // Check to see if the chapter stops have the right labels.
+ index = BackForwardMenuModel::kMaxHistoryItems;
+ // Empty string indicates item is a separator.
+ EXPECT_EQ(ASCIIToUTF16(""), forward_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("E3"), forward_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("F3"), forward_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("G3"), forward_model->GetLabelAt(index++));
+ EXPECT_EQ(ASCIIToUTF16("H3"), forward_model->GetLabelAt(index++));
+ // The menu should only show a maximum of 5 chapter stops.
+ EXPECT_EQ(ASCIIToUTF16("I3"), forward_model->GetLabelAt(index));
+ // Empty string indicates item is a separator.
+ EXPECT_EQ(ASCIIToUTF16(""), forward_model->GetLabelAt(index + 1));
+ EXPECT_EQ(forward_model->GetShowFullHistoryLabel(),
+ forward_model->GetLabelAt(index + 2));
+
+ // If we advance one we should still see the same chapter stop at the end.
+ GoForward();
+ EXPECT_EQ(ASCIIToUTF16("I3"), forward_model->GetLabelAt(index));
+ // But if we advance one again, it should change.
+ GoForward();
+ EXPECT_EQ(ASCIIToUTF16("J3"), forward_model->GetLabelAt(index));
+ GoForward();
+ EXPECT_EQ(ASCIIToUTF16("J3"), forward_model->GetLabelAt(index));
+ GoForward();
+ EXPECT_EQ(ASCIIToUTF16("J3"), forward_model->GetLabelAt(index));
+ GoForward();
+ EXPECT_EQ(ASCIIToUTF16("K3"), forward_model->GetLabelAt(index));
+
+ // Now test the boundary cases by using the chapter stop function directly.
+ // Out of bounds, first too far right (incrementing), then too far left.
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(33, false));
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(-1, true));
+ // Test being at end and going right, then at beginning going left.
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(32, true));
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(0, false));
+ // Test success: beginning going right and end going left.
+ EXPECT_EQ(2, back_model->GetIndexOfNextChapterStop(0, true));
+ EXPECT_EQ(29, back_model->GetIndexOfNextChapterStop(32, false));
+ // Now see when the chapter stops begin to show up.
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(1, false));
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(2, false));
+ EXPECT_EQ(2, back_model->GetIndexOfNextChapterStop(3, false));
+ // Now see when the chapter stops end.
+ EXPECT_EQ(32, back_model->GetIndexOfNextChapterStop(30, true));
+ EXPECT_EQ(32, back_model->GetIndexOfNextChapterStop(31, true));
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(32, true));
+
+ // Bug found during review (two different sites, but first wasn't considered
+ // a chapter-stop).
+ // Go to A1;
+ NavigateToIndex(0);
+ LoadURLAndUpdateState("http://www.b.com/1", "B1");
+ EXPECT_EQ(0, back_model->GetIndexOfNextChapterStop(1, false));
+ EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(0, true));
+
+ // Now see if it counts 'www.x.com' and 'mail.x.com' as same domain, which
+ // it should.
+ // Go to A1.
+ NavigateToIndex(0);
+ LoadURLAndUpdateState("http://mail.a.com/2", "A2-mai");
+ LoadURLAndUpdateState("http://www.b.com/1", "B1");
+ LoadURLAndUpdateState("http://mail.b.com/2", "B2-mai");
+ LoadURLAndUpdateState("http://new.site.com", "new");
+ EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(0, true));
+ EXPECT_EQ(3, back_model->GetIndexOfNextChapterStop(1, true));
+ EXPECT_EQ(3, back_model->GetIndexOfNextChapterStop(2, true));
+ EXPECT_EQ(4, back_model->GetIndexOfNextChapterStop(3, true));
+ // And try backwards as well.
+ EXPECT_EQ(3, back_model->GetIndexOfNextChapterStop(4, false));
+ EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(3, false));
+ EXPECT_EQ(1, back_model->GetIndexOfNextChapterStop(2, false));
+ EXPECT_EQ(-1, back_model->GetIndexOfNextChapterStop(1, false));
+}
diff --git a/chrome/browser/ui/toolbar/encoding_menu_controller.cc b/chrome/browser/ui/toolbar/encoding_menu_controller.cc
new file mode 100644
index 0000000..c93fb1e
--- /dev/null
+++ b/chrome/browser/ui/toolbar/encoding_menu_controller.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
+
+#include "app/l10n_util.h"
+#include "base/i18n/rtl.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/character_encoding.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "grit/generated_resources.h"
+
+const int EncodingMenuController::kValidEncodingIds[] = {
+ IDC_ENCODING_UTF8,
+ IDC_ENCODING_UTF16LE,
+ IDC_ENCODING_ISO88591,
+ IDC_ENCODING_WINDOWS1252,
+ IDC_ENCODING_GBK,
+ IDC_ENCODING_GB18030,
+ IDC_ENCODING_BIG5,
+ IDC_ENCODING_BIG5HKSCS,
+ IDC_ENCODING_KOREAN,
+ IDC_ENCODING_SHIFTJIS,
+ IDC_ENCODING_ISO2022JP,
+ IDC_ENCODING_EUCJP,
+ IDC_ENCODING_THAI,
+ IDC_ENCODING_ISO885915,
+ IDC_ENCODING_MACINTOSH,
+ IDC_ENCODING_ISO88592,
+ IDC_ENCODING_WINDOWS1250,
+ IDC_ENCODING_ISO88595,
+ IDC_ENCODING_WINDOWS1251,
+ IDC_ENCODING_KOI8R,
+ IDC_ENCODING_KOI8U,
+ IDC_ENCODING_ISO88597,
+ IDC_ENCODING_WINDOWS1253,
+ IDC_ENCODING_ISO88594,
+ IDC_ENCODING_ISO885913,
+ IDC_ENCODING_WINDOWS1257,
+ IDC_ENCODING_ISO88593,
+ IDC_ENCODING_ISO885910,
+ IDC_ENCODING_ISO885914,
+ IDC_ENCODING_ISO885916,
+ IDC_ENCODING_WINDOWS1254,
+ IDC_ENCODING_ISO88596,
+ IDC_ENCODING_WINDOWS1256,
+ IDC_ENCODING_ISO88598,
+ IDC_ENCODING_WINDOWS1255,
+ IDC_ENCODING_WINDOWS1258,
+ IDC_ENCODING_ISO88598I,
+};
+
+bool EncodingMenuController::DoesCommandBelongToEncodingMenu(int id) {
+ if (id == IDC_ENCODING_AUTO_DETECT) {
+ return true;
+ }
+
+ for (size_t i = 0; i < arraysize(kValidEncodingIds); ++i) {
+ if (id == kValidEncodingIds[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const int* EncodingMenuController::ValidGUIEncodingIDs() {
+ return kValidEncodingIds;
+}
+
+int EncodingMenuController::NumValidGUIEncodingIDs() {
+ return arraysize(kValidEncodingIds);
+}
+
+bool EncodingMenuController::IsItemChecked(
+ Profile* browser_profile,
+ const std::string& current_tab_encoding,
+ int item_id) {
+ if (!DoesCommandBelongToEncodingMenu(item_id))
+ return false;
+
+ std::string encoding = current_tab_encoding;
+ if (encoding.empty())
+ encoding = browser_profile->GetPrefs()->GetString(prefs::kDefaultCharset);
+
+ if (item_id == IDC_ENCODING_AUTO_DETECT) {
+ return browser_profile->GetPrefs()->GetBoolean(
+ prefs::kWebKitUsesUniversalDetector);
+ }
+
+ if (!encoding.empty()) {
+ return encoding ==
+ CharacterEncoding::GetCanonicalEncodingNameByCommandId(item_id);
+ }
+
+ return false;
+}
+
+void EncodingMenuController::GetEncodingMenuItems(Profile* profile,
+ EncodingMenuItemList* menuItems) {
+
+ DCHECK(menuItems);
+ EncodingMenuItem separator(0, string16());
+
+ menuItems->clear();
+ menuItems->push_back(
+ EncodingMenuItem(IDC_ENCODING_AUTO_DETECT,
+ l10n_util::GetStringUTF16(IDS_ENCODING_AUTO_DETECT)));
+ menuItems->push_back(separator);
+
+ // Create current display encoding list.
+ const std::vector<CharacterEncoding::EncodingInfo>* encodings;
+
+ // Build the list of encoding ids : It is made of the
+ // locale-dependent short list, the cache of recently selected
+ // encodings and other encodings.
+ encodings = CharacterEncoding::GetCurrentDisplayEncodings(
+ g_browser_process->GetApplicationLocale(),
+ profile->GetPrefs()->GetString(prefs::kStaticEncodings),
+ profile->GetPrefs()->GetString(prefs::kRecentlySelectedEncoding));
+ DCHECK(encodings);
+ DCHECK(!encodings->empty());
+
+ // Build up output list for menu.
+ std::vector<CharacterEncoding::EncodingInfo>::const_iterator it;
+ for (it = encodings->begin(); it != encodings->end(); ++it) {
+ if (it->encoding_id) {
+ std::wstring encoding = it->encoding_display_name;
+ base::i18n::AdjustStringForLocaleDirection(&encoding);
+ menuItems->push_back(EncodingMenuItem(it->encoding_id,
+ WideToUTF16(encoding)));
+ } else {
+ menuItems->push_back(separator);
+ }
+ }
+}
diff --git a/chrome/browser/ui/toolbar/encoding_menu_controller.h b/chrome/browser/ui/toolbar/encoding_menu_controller.h
new file mode 100644
index 0000000..bfa8c57
--- /dev/null
+++ b/chrome/browser/ui/toolbar/encoding_menu_controller.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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_ENCODING_MENU_CONTROLLER_H_
+#define CHROME_BROWSER_UI_TOOLBAR_ENCODING_MENU_CONTROLLER_H_
+#pragma once
+
+#include <utility>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h" // For DISALLOW_COPY_AND_ASSIGN
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+
+class Profile;
+
+// Cross-platform logic needed for the encoding menu.
+// For now, we don't need to track state so all methods are static.
+class EncodingMenuController {
+ FRIEND_TEST_ALL_PREFIXES(EncodingMenuControllerTest, EncodingIDsBelongTest);
+ FRIEND_TEST_ALL_PREFIXES(EncodingMenuControllerTest, IsItemChecked);
+
+ public:
+ typedef std::pair<int, string16> EncodingMenuItem;
+ typedef std::vector<EncodingMenuItem> EncodingMenuItemList;
+
+ public:
+ EncodingMenuController() {}
+
+ // Given a command ID, does this command belong to the encoding menu?
+ bool DoesCommandBelongToEncodingMenu(int id);
+
+ // Returns true if the given encoding menu item (specified by item_id)
+ // is checked. Note that this header is included from objc, where the name
+ // "id" is reserved.
+ bool IsItemChecked(Profile* browser_profile,
+ const std::string& current_tab_encoding,
+ int item_id);
+
+ // Fills in a list of menu items in the order they should appear in the menu.
+ // Items whose ids are 0 are separators.
+ void GetEncodingMenuItems(Profile* profile,
+ EncodingMenuItemList* menuItems);
+
+ private:
+ // List of all valid encoding GUI IDs.
+ static const int kValidEncodingIds[];
+ const int* ValidGUIEncodingIDs();
+ int NumValidGUIEncodingIDs();
+
+ DISALLOW_COPY_AND_ASSIGN(EncodingMenuController);
+};
+
+#endif // CHROME_BROWSER_UI_TOOLBAR_ENCODING_MENU_CONTROLLER_H_
diff --git a/chrome/browser/ui/toolbar/encoding_menu_controller_unittest.cc b/chrome/browser/ui/toolbar/encoding_menu_controller_unittest.cc
new file mode 100644
index 0000000..13fa63c
--- /dev/null
+++ b/chrome/browser/ui/toolbar/encoding_menu_controller_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+class EncodingMenuControllerTest : public testing::Test {
+};
+
+TEST_F(EncodingMenuControllerTest, EncodingIDsBelongTest) {
+ EncodingMenuController controller;
+
+ // Check some bogus ids to make sure they're never valid.
+ ASSERT_FALSE(controller.DoesCommandBelongToEncodingMenu(0));
+ ASSERT_FALSE(controller.DoesCommandBelongToEncodingMenu(-1));
+
+ int num_valid_encoding_ids = controller.NumValidGUIEncodingIDs();
+ const int* valid_encodings = controller.ValidGUIEncodingIDs();
+ ASSERT_TRUE(controller.DoesCommandBelongToEncodingMenu(
+ IDC_ENCODING_AUTO_DETECT));
+ // Check that all valid encodings are accepted.
+ for (int i = 0; i < num_valid_encoding_ids; ++i) {
+ ASSERT_TRUE(controller.DoesCommandBelongToEncodingMenu(valid_encodings[i]));
+ }
+
+ // This test asserts that we haven't added a new valid ID without including it
+ // in the kValidEncodingIds test list above.
+ // The premise is that new encodings will be added directly after the current
+ // ones so we'll catch such cases.
+ int one_past_largest_id = valid_encodings[num_valid_encoding_ids - 1] + 1;
+ ASSERT_FALSE(controller.DoesCommandBelongToEncodingMenu(one_past_largest_id));
+}
+
+TEST_F(EncodingMenuControllerTest, ListEncodingMenuItems) {
+ typedef EncodingMenuController::EncodingMenuItemList EncodingMenuItemList;
+ EncodingMenuController controller;
+
+ EncodingMenuItemList english_items;
+ TestingProfile profile_en;
+
+ controller.GetEncodingMenuItems(&profile_en, &english_items);
+
+ // Make sure there are items in the lists.
+ ASSERT_TRUE(english_items.size() > 0);
+ // Make sure that autodetect is the first item on both menus
+ ASSERT_EQ(english_items[0].first, IDC_ENCODING_AUTO_DETECT);
+}
+
+TEST_F(EncodingMenuControllerTest, IsItemChecked) {
+ TestingProfile profile_en;
+ std::string encoding("UTF-8");
+
+ // Check that enabling and disabling autodetect works.
+ bool autodetect_enabed[] = {true, false};
+ PrefService* prefs = profile_en.GetPrefs();
+ EncodingMenuController controller;
+ for (size_t i = 0; i < arraysize(autodetect_enabed); ++i) {
+ bool enabled = autodetect_enabed[i];
+ prefs->SetBoolean(prefs::kWebKitUsesUniversalDetector, enabled);
+ ASSERT_TRUE(controller.IsItemChecked(&profile_en,
+ encoding,
+ IDC_ENCODING_AUTO_DETECT) == enabled);
+ }
+
+ // Check all valid encodings, make sure only one is enabled when autodetection
+ // is turned off.
+ prefs->SetBoolean(prefs::kWebKitUsesUniversalDetector, false);
+ bool encoding_is_enabled = false;
+ size_t num_valid_encoding_ids = controller.NumValidGUIEncodingIDs();
+ const int* valid_encodings = controller.ValidGUIEncodingIDs();
+ for (size_t i = 0; i < num_valid_encoding_ids; ++i) {
+ bool on = controller.IsItemChecked(&profile_en,
+ encoding,
+ valid_encodings[i]);
+ // Only one item in the encoding menu can be selected at a time.
+ ASSERT_FALSE(on && encoding_is_enabled);
+ encoding_is_enabled |= on;
+ }
+
+ // Make sure at least one encoding is enabled.
+ ASSERT_TRUE(encoding_is_enabled);
+}
diff --git a/chrome/browser/ui/toolbar/toolbar_model.cc b/chrome/browser/ui/toolbar/toolbar_model.cc
new file mode 100644
index 0000000..5a6a97c
--- /dev/null
+++ b/chrome/browser/ui/toolbar/toolbar_model.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/toolbar_model.h"
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autocomplete/autocomplete.h"
+#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ssl/ssl_error_info.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/net_util.h"
+
+ToolbarModel::ToolbarModel(Browser* browser)
+ : browser_(browser),
+ input_in_progress_(false) {
+}
+
+ToolbarModel::~ToolbarModel() {
+}
+
+// ToolbarModel Implementation.
+std::wstring ToolbarModel::GetText() const {
+ GURL url(chrome::kAboutBlankURL);
+ std::string languages; // Empty if we don't have a |navigation_controller|.
+
+ NavigationController* navigation_controller = GetNavigationController();
+ if (navigation_controller) {
+ languages = navigation_controller->profile()->GetPrefs()->GetString(
+ prefs::kAcceptLanguages);
+ NavigationEntry* entry = navigation_controller->GetActiveEntry();
+ if (!navigation_controller->tab_contents()->ShouldDisplayURL()) {
+ // Explicitly hide the URL for this tab.
+ url = GURL();
+ } else if (entry) {
+ url = entry->virtual_url();
+ }
+ }
+ if (url.spec().length() > chrome::kMaxURLDisplayChars)
+ url = url.IsStandard() ? url.GetOrigin() : GURL(url.scheme() + ":");
+ // Note that we can't unescape spaces here, because if the user copies this
+ // and pastes it into another program, that program may think the URL ends at
+ // the space.
+ return AutocompleteInput::FormattedStringWithEquivalentMeaning(url,
+ UTF16ToWideHack(net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
+ UnescapeRule::NORMAL, NULL, NULL, NULL)));
+}
+
+ToolbarModel::SecurityLevel ToolbarModel::GetSecurityLevel() const {
+ if (input_in_progress_) // When editing, assume no security style.
+ return NONE;
+
+ NavigationController* navigation_controller = GetNavigationController();
+ if (!navigation_controller) // We might not have a controller on init.
+ return NONE;
+
+ NavigationEntry* entry = navigation_controller->GetActiveEntry();
+ if (!entry)
+ return NONE;
+
+ const NavigationEntry::SSLStatus& ssl = entry->ssl();
+ switch (ssl.security_style()) {
+ case SECURITY_STYLE_UNKNOWN:
+ case SECURITY_STYLE_UNAUTHENTICATED:
+ return NONE;
+
+ case SECURITY_STYLE_AUTHENTICATION_BROKEN:
+ return SECURITY_ERROR;
+
+ case SECURITY_STYLE_AUTHENTICATED:
+ if (ssl.displayed_insecure_content())
+ return SECURITY_WARNING;
+ if (net::IsCertStatusError(ssl.cert_status())) {
+ DCHECK_EQ(ssl.cert_status() & net::CERT_STATUS_ALL_ERRORS,
+ net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION);
+ return SECURITY_WARNING;
+ }
+ if ((ssl.cert_status() & net::CERT_STATUS_IS_EV) &&
+ CertStore::GetInstance()->RetrieveCert(ssl.cert_id(), NULL))
+ return EV_SECURE;
+ return SECURE;
+
+ default:
+ NOTREACHED();
+ return NONE;
+ }
+}
+
+int ToolbarModel::GetIcon() const {
+ static int icon_ids[NUM_SECURITY_LEVELS] = {
+ IDR_OMNIBOX_HTTP,
+ IDR_OMNIBOX_HTTPS_VALID,
+ IDR_OMNIBOX_HTTPS_VALID,
+ IDR_OMNIBOX_HTTPS_WARNING,
+ IDR_OMNIBOX_HTTPS_INVALID,
+ };
+ DCHECK(arraysize(icon_ids) == NUM_SECURITY_LEVELS);
+ return icon_ids[GetSecurityLevel()];
+}
+
+std::wstring ToolbarModel::GetEVCertName() const {
+ DCHECK_EQ(GetSecurityLevel(), EV_SECURE);
+ scoped_refptr<net::X509Certificate> cert;
+ // Note: Navigation controller and active entry are guaranteed non-NULL or
+ // the security level would be NONE.
+ CertStore::GetInstance()->RetrieveCert(
+ GetNavigationController()->GetActiveEntry()->ssl().cert_id(), &cert);
+ return SSLManager::GetEVCertName(*cert);
+}
+
+NavigationController* ToolbarModel::GetNavigationController() const {
+ // This |current_tab| can be NULL during the initialization of the
+ // toolbar during window creation (i.e. before any tabs have been added
+ // to the window).
+ TabContents* current_tab = browser_->GetSelectedTabContents();
+ return current_tab ? &current_tab->controller() : NULL;
+}
diff --git a/chrome/browser/ui/toolbar/toolbar_model.h b/chrome/browser/ui/toolbar/toolbar_model.h
new file mode 100644
index 0000000..d9ebec5
--- /dev/null
+++ b/chrome/browser/ui/toolbar/toolbar_model.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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_TOOLBAR_MODEL_H_
+#define CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_MODEL_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class Browser;
+class NavigationController;
+
+// This class is the model used by the toolbar, location bar and autocomplete
+// edit. It populates its states from the current navigation entry retrieved
+// from the navigation controller returned by GetNavigationController().
+class ToolbarModel {
+ public:
+ // TODO(wtc): unify ToolbarModel::SecurityLevel with SecurityStyle. We
+ // don't need two sets of security UI levels. SECURITY_STYLE_AUTHENTICATED
+ // needs to be refined into three levels: warning, standard, and EV.
+ enum SecurityLevel {
+ NONE = 0, // HTTP/no URL/user is editing
+ EV_SECURE, // HTTPS with valid EV cert
+ SECURE, // HTTPS (non-EV)
+ SECURITY_WARNING, // HTTPS, but unable to check certificate revocation
+ // status or with insecure content on the page
+ SECURITY_ERROR, // Attempted HTTPS and failed, page not authenticated
+ NUM_SECURITY_LEVELS,
+ };
+
+ explicit ToolbarModel(Browser* browser);
+ ~ToolbarModel();
+
+ // Returns the text that should be displayed in the location bar.
+ std::wstring GetText() const;
+
+ // Returns the security level that the toolbar should display.
+ SecurityLevel GetSecurityLevel() const;
+
+ // Returns the resource_id of the icon to show to the left of the address,
+ // based on the current URL. This doesn't cover specialized icons while the
+ // user is editing; see AutocompleteEditView::GetIcon().
+ int GetIcon() const;
+
+ // Returns the name of the EV cert holder. Only call this when the security
+ // level is EV_SECURE.
+ std::wstring GetEVCertName() const;
+
+ // Getter/setter of whether the text in location bar is currently being
+ // edited.
+ void set_input_in_progress(bool value) { input_in_progress_ = value; }
+ bool input_in_progress() const { return input_in_progress_; }
+
+ private:
+ // Returns the navigation controller used to retrieve the navigation entry
+ // from which the states are retrieved.
+ // If this returns NULL, default values are used.
+ NavigationController* GetNavigationController() const;
+
+ Browser* browser_;
+
+ // Whether the text in the location bar is currently being edited.
+ bool input_in_progress_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ToolbarModel);
+};
+
+#endif // CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_MODEL_H_
diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.cc b/chrome/browser/ui/toolbar/wrench_menu_model.cc
new file mode 100644
index 0000000..cd8a114
--- /dev/null
+++ b/chrome/browser/ui/toolbar/wrench_menu_model.cc
@@ -0,0 +1,536 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/wrench_menu_model.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "app/l10n_util.h"
+#include "app/menus/button_menu_item_model.h"
+#include "app/resource_bundle.h"
+#include "base/command_line.h"
+#include "base/i18n/number_formatting.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/background_page_tracker.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/sync_ui_util.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
+#include "chrome/browser/upgrade_detector.h"
+#include "chrome/common/badge_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+
+#if defined(OS_LINUX)
+#include <gtk/gtk.h>
+#include "chrome/browser/gtk/gtk_util.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "chrome/browser/ui/browser_window.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/cros/update_library.h"
+#endif
+
+#if defined(OS_WIN)
+#include "chrome/browser/enumerate_modules_model_win.h"
+#endif
+
+// The size of the font used for dynamic text overlays on menu items.
+const float kMenuBadgeFontSize = 12.0;
+
+namespace {
+SkBitmap GetBackgroundPageIcon() {
+ string16 pages = base::FormatNumber(
+ BackgroundPageTracker::GetInstance()->GetBackgroundPageCount());
+ return badge_util::DrawBadgeIconOverlay(
+ *ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_BACKGROUND_MENU),
+ kMenuBadgeFontSize,
+ pages,
+ l10n_util::GetStringUTF16(IDS_BACKGROUND_PAGE_BADGE_OVERFLOW));
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// EncodingMenuModel
+
+EncodingMenuModel::EncodingMenuModel(Browser* browser)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(menus::SimpleMenuModel(this)),
+ browser_(browser) {
+ Build();
+}
+
+EncodingMenuModel::~EncodingMenuModel() {
+}
+
+void EncodingMenuModel::Build() {
+ EncodingMenuController::EncodingMenuItemList encoding_menu_items;
+ EncodingMenuController encoding_menu_controller;
+ encoding_menu_controller.GetEncodingMenuItems(browser_->profile(),
+ &encoding_menu_items);
+
+ int group_id = 0;
+ EncodingMenuController::EncodingMenuItemList::iterator it =
+ encoding_menu_items.begin();
+ for (; it != encoding_menu_items.end(); ++it) {
+ int id = it->first;
+ string16& label = it->second;
+ if (id == 0) {
+ AddSeparator();
+ } else {
+ if (id == IDC_ENCODING_AUTO_DETECT) {
+ AddCheckItem(id, label);
+ } else {
+ // Use the id of the first radio command as the id of the group.
+ if (group_id <= 0)
+ group_id = id;
+ AddRadioItem(id, label, group_id);
+ }
+ }
+ }
+}
+
+bool EncodingMenuModel::IsCommandIdChecked(int command_id) const {
+ TabContents* current_tab = browser_->GetSelectedTabContents();
+ if (!current_tab)
+ return false;
+ EncodingMenuController controller;
+ return controller.IsItemChecked(browser_->profile(),
+ current_tab->encoding(), command_id);
+}
+
+bool EncodingMenuModel::IsCommandIdEnabled(int command_id) const {
+ bool enabled = browser_->command_updater()->IsCommandEnabled(command_id);
+ // Special handling for the contents of the Encoding submenu. On Mac OS,
+ // instead of enabling/disabling the top-level menu item, the submenu's
+ // contents get disabled, per Apple's HIG.
+#if defined(OS_MACOSX)
+ enabled &= browser_->command_updater()->IsCommandEnabled(IDC_ENCODING_MENU);
+#endif
+ return enabled;
+}
+
+bool EncodingMenuModel::GetAcceleratorForCommandId(
+ int command_id,
+ menus::Accelerator* accelerator) {
+ return false;
+}
+
+void EncodingMenuModel::ExecuteCommand(int command_id) {
+ browser_->ExecuteCommand(command_id);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ZoomMenuModel
+
+ZoomMenuModel::ZoomMenuModel(menus::SimpleMenuModel::Delegate* delegate)
+ : SimpleMenuModel(delegate) {
+ Build();
+}
+
+ZoomMenuModel::~ZoomMenuModel() {
+}
+
+void ZoomMenuModel::Build() {
+ AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS);
+ AddItemWithStringId(IDC_ZOOM_NORMAL, IDS_ZOOM_NORMAL);
+ AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToolsMenuModel
+
+ToolsMenuModel::ToolsMenuModel(menus::SimpleMenuModel::Delegate* delegate,
+ Browser* browser)
+ : SimpleMenuModel(delegate) {
+ Build(browser);
+}
+
+ToolsMenuModel::~ToolsMenuModel() {}
+
+void ToolsMenuModel::Build(Browser* browser) {
+ AddCheckItemWithStringId(IDC_SHOW_BOOKMARK_BAR, IDS_SHOW_BOOKMARK_BAR);
+
+ AddSeparator();
+
+#if !defined(OS_CHROMEOS)
+#if defined(OS_MACOSX)
+ AddItemWithStringId(IDC_CREATE_SHORTCUTS, IDS_CREATE_APPLICATION_MAC);
+#else
+ AddItemWithStringId(IDC_CREATE_SHORTCUTS, IDS_CREATE_SHORTCUTS);
+#endif
+ AddSeparator();
+#endif
+
+ AddItemWithStringId(IDC_MANAGE_EXTENSIONS, IDS_SHOW_EXTENSIONS);
+ AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
+ AddItemWithStringId(IDC_CLEAR_BROWSING_DATA, IDS_CLEAR_BROWSING_DATA);
+
+ AddSeparator();
+#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_LINUX)
+ AddItemWithStringId(IDC_FEEDBACK, IDS_FEEDBACK);
+ AddSeparator();
+#endif
+
+ encoding_menu_model_.reset(new EncodingMenuModel(browser));
+ AddSubMenuWithStringId(IDC_ENCODING_MENU, IDS_ENCODING_MENU,
+ encoding_menu_model_.get());
+ AddItemWithStringId(IDC_VIEW_SOURCE, IDS_VIEW_SOURCE);
+ if (g_browser_process->have_inspector_files()) {
+ AddItemWithStringId(IDC_DEV_TOOLS, IDS_DEV_TOOLS);
+ AddItemWithStringId(IDC_DEV_TOOLS_CONSOLE, IDS_DEV_TOOLS_CONSOLE);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WrenchMenuModel
+
+WrenchMenuModel::WrenchMenuModel(menus::AcceleratorProvider* provider,
+ Browser* browser)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(menus::SimpleMenuModel(this)),
+ provider_(provider),
+ browser_(browser),
+ tabstrip_model_(browser_->tabstrip_model()) {
+ Build();
+ UpdateZoomControls();
+
+ tabstrip_model_->AddObserver(this);
+
+ registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED,
+ Source<Profile>(browser_->profile()));
+ registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
+ NotificationService::AllSources());
+}
+
+WrenchMenuModel::~WrenchMenuModel() {
+ if (tabstrip_model_)
+ tabstrip_model_->RemoveObserver(this);
+}
+
+bool WrenchMenuModel::DoesCommandIdDismissMenu(int command_id) const {
+ return command_id != IDC_ZOOM_MINUS && command_id != IDC_ZOOM_PLUS;
+}
+
+bool WrenchMenuModel::IsItemForCommandIdDynamic(int command_id) const {
+ return command_id == IDC_ZOOM_PERCENT_DISPLAY ||
+#if defined(OS_MACOSX)
+ command_id == IDC_FULLSCREEN ||
+#endif
+ command_id == IDC_SYNC_BOOKMARKS ||
+ command_id == IDC_VIEW_BACKGROUND_PAGES;
+}
+
+bool WrenchMenuModel::GetIconForCommandId(int command_id,
+ SkBitmap* bitmap) const {
+ switch (command_id) {
+ case IDC_VIEW_BACKGROUND_PAGES: {
+ int num_pages = BackgroundPageTracker::GetInstance()->
+ GetUnacknowledgedBackgroundPageCount();
+ if (num_pages > 0) {
+ *bitmap = GetBackgroundPageIcon();
+ return true;
+ } else {
+ // No icon.
+ return false;
+ }
+ }
+ default:
+ // No icon for other dynamic menu items.
+ return false;
+ }
+}
+
+string16 WrenchMenuModel::GetLabelForCommandId(int command_id) const {
+ switch (command_id) {
+ case IDC_SYNC_BOOKMARKS:
+ return GetSyncMenuLabel();
+ case IDC_ZOOM_PERCENT_DISPLAY:
+ return zoom_label_;
+#if defined(OS_MACOSX)
+ case IDC_FULLSCREEN: {
+ int string_id = IDS_ENTER_FULLSCREEN_MAC; // Default to Enter.
+ // Note: On startup, |window()| may be NULL.
+ if (browser_->window() && browser_->window()->IsFullscreen())
+ string_id = IDS_EXIT_FULLSCREEN_MAC;
+ return l10n_util::GetStringUTF16(string_id);
+ }
+#endif
+ case IDC_VIEW_BACKGROUND_PAGES: {
+ string16 num_background_pages = base::FormatNumber(
+ BackgroundPageTracker::GetInstance()->GetBackgroundPageCount());
+ return l10n_util::GetStringFUTF16(IDS_VIEW_BACKGROUND_PAGES,
+ num_background_pages);
+ }
+ default:
+ NOTREACHED();
+ return string16();
+ }
+}
+
+void WrenchMenuModel::ExecuteCommand(int command_id) {
+ browser_->ExecuteCommand(command_id);
+}
+
+bool WrenchMenuModel::IsCommandIdChecked(int command_id) const {
+ if (command_id == IDC_SHOW_BOOKMARK_BAR) {
+ return browser_->profile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
+ }
+
+ return false;
+}
+
+bool WrenchMenuModel::IsCommandIdEnabled(int command_id) const {
+#if defined(OS_CHROMEOS)
+ // Special case because IDC_NEW_WINDOW item should be disabled in BWSI mode,
+ // but accelerator should work.
+ if (command_id == IDC_NEW_WINDOW &&
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession))
+ return false;
+#endif
+
+ return browser_->command_updater()->IsCommandEnabled(command_id);
+}
+
+bool WrenchMenuModel::IsCommandIdVisible(int command_id) const {
+ if (command_id == IDC_UPGRADE_DIALOG) {
+#if defined(OS_CHROMEOS)
+ return (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->status().status
+ == chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT);
+#else
+ return UpgradeDetector::GetInstance()->notify_upgrade();
+#endif
+ } else if (command_id == IDC_VIEW_INCOMPATIBILITIES) {
+#if defined(OS_WIN)
+ EnumerateModulesModel* loaded_modules =
+ EnumerateModulesModel::GetInstance();
+ return loaded_modules->confirmed_bad_modules_detected() > 0;
+#else
+ return false;
+#endif
+ } else if (command_id == IDC_VIEW_BACKGROUND_PAGES) {
+ BackgroundPageTracker* tracker = BackgroundPageTracker::GetInstance();
+ return tracker->GetBackgroundPageCount() > 0;
+ }
+ return true;
+}
+
+bool WrenchMenuModel::GetAcceleratorForCommandId(
+ int command_id,
+ menus::Accelerator* accelerator) {
+ return provider_->GetAcceleratorForCommandId(command_id, accelerator);
+}
+
+void WrenchMenuModel::TabSelectedAt(TabContentsWrapper* old_contents,
+ TabContentsWrapper* new_contents,
+ int index,
+ bool user_gesture) {
+ // The user has switched between tabs and the new tab may have a different
+ // zoom setting.
+ UpdateZoomControls();
+}
+
+void WrenchMenuModel::TabReplacedAt(TabContentsWrapper* old_contents,
+ TabContentsWrapper* new_contents,
+ int index) {
+ UpdateZoomControls();
+}
+
+void WrenchMenuModel::TabStripModelDeleted() {
+ // During views shutdown, the tabstrip model/browser is deleted first, while
+ // it is the opposite in gtk land.
+ tabstrip_model_->RemoveObserver(this);
+ tabstrip_model_ = NULL;
+}
+
+void WrenchMenuModel::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::ZOOM_LEVEL_CHANGED:
+ case NotificationType::NAV_ENTRY_COMMITTED:
+ UpdateZoomControls();
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+// For testing.
+WrenchMenuModel::WrenchMenuModel()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(menus::SimpleMenuModel(this)),
+ provider_(NULL),
+ browser_(NULL),
+ tabstrip_model_(NULL) {
+}
+
+void WrenchMenuModel::Build() {
+ AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB);
+ AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW);
+ AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW,
+ IDS_NEW_INCOGNITO_WINDOW);
+
+ AddSeparator();
+#if defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(TOOLKIT_VIEWS))
+ // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the
+ // layout for this menu item in Toolbar.xib. It does, however, use the
+ // command_id value from AddButtonItem() to identify this special item.
+ edit_menu_item_model_.reset(new menus::ButtonMenuItemModel(IDS_EDIT, this));
+ edit_menu_item_model_->AddGroupItemWithStringId(IDC_CUT, IDS_CUT);
+ edit_menu_item_model_->AddGroupItemWithStringId(IDC_COPY, IDS_COPY);
+ edit_menu_item_model_->AddGroupItemWithStringId(IDC_PASTE, IDS_PASTE);
+ AddButtonItem(IDC_EDIT_MENU, edit_menu_item_model_.get());
+#else
+ // TODO(port): Move to the above.
+ CreateCutCopyPaste();
+#endif
+
+ AddSeparator();
+#if defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(TOOLKIT_VIEWS))
+ // WARNING: See above comment.
+ zoom_menu_item_model_.reset(
+ new menus::ButtonMenuItemModel(IDS_ZOOM_MENU, this));
+ zoom_menu_item_model_->AddGroupItemWithStringId(
+ IDC_ZOOM_MINUS, IDS_ZOOM_MINUS2);
+ zoom_menu_item_model_->AddButtonLabel(IDC_ZOOM_PERCENT_DISPLAY,
+ IDS_ZOOM_PLUS2);
+ zoom_menu_item_model_->AddGroupItemWithStringId(
+ IDC_ZOOM_PLUS, IDS_ZOOM_PLUS2);
+ zoom_menu_item_model_->AddSpace();
+ zoom_menu_item_model_->AddItemWithImage(
+ IDC_FULLSCREEN, IDR_FULLSCREEN_MENU_BUTTON);
+ AddButtonItem(IDC_ZOOM_MENU, zoom_menu_item_model_.get());
+#else
+ // TODO(port): Move to the above.
+ CreateZoomFullscreen();
+#endif
+
+ AddSeparator();
+ AddItemWithStringId(IDC_SAVE_PAGE, IDS_SAVE_PAGE);
+ AddItemWithStringId(IDC_FIND, IDS_FIND);
+ AddItemWithStringId(IDC_PRINT, IDS_PRINT);
+
+ tools_menu_model_.reset(new ToolsMenuModel(this, browser_));
+ AddSubMenuWithStringId(IDC_ZOOM_MENU, IDS_TOOLS_MENU,
+ tools_menu_model_.get());
+
+ AddSeparator();
+#if defined(ENABLE_REMOTING)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableRemoting)) {
+ AddItem(IDC_REMOTING_SETUP,
+ l10n_util::GetStringUTF16(IDS_REMOTING_SETUP_LABEL));
+ }
+#endif
+ AddItemWithStringId(IDC_SHOW_BOOKMARK_MANAGER, IDS_BOOKMARK_MANAGER);
+ AddItemWithStringId(IDC_SHOW_HISTORY, IDS_SHOW_HISTORY);
+ AddItemWithStringId(IDC_SHOW_DOWNLOADS, IDS_SHOW_DOWNLOADS);
+ AddSeparator();
+
+#if defined(OS_CHROMEOS)
+ AddItemWithStringId(IDC_OPTIONS, IDS_SETTINGS);
+#elif defined(OS_MACOSX)
+ AddItemWithStringId(IDC_OPTIONS, IDS_PREFERENCES);
+#elif defined(OS_LINUX)
+ string16 preferences = gtk_util::GetStockPreferencesMenuLabel();
+ if (!preferences.empty())
+ AddItem(IDC_OPTIONS, preferences);
+ else
+ AddItemWithStringId(IDC_OPTIONS, IDS_PREFERENCES);
+#else
+ AddItemWithStringId(IDC_OPTIONS, IDS_OPTIONS);
+#endif
+
+#if defined(OS_CHROMEOS)
+ const string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME);
+#else
+ const string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
+#endif
+ // On Mac, there is no About item.
+ if (browser_defaults::kShowAboutMenuItem) {
+ AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(
+ IDS_ABOUT, product_name));
+ }
+ string16 num_background_pages = base::FormatNumber(
+ BackgroundPageTracker::GetInstance()->GetBackgroundPageCount());
+ AddItem(IDC_VIEW_BACKGROUND_PAGES, l10n_util::GetStringFUTF16(
+ IDS_VIEW_BACKGROUND_PAGES, num_background_pages));
+ AddItem(IDC_UPGRADE_DIALOG, l10n_util::GetStringFUTF16(
+ IDS_UPDATE_NOW, product_name));
+ AddItem(IDC_VIEW_INCOMPATIBILITIES, l10n_util::GetStringUTF16(
+ IDS_VIEW_INCOMPATIBILITIES));
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ SetIcon(GetIndexOfCommandId(IDC_UPGRADE_DIALOG),
+ *rb.GetBitmapNamed(IDR_UPDATE_MENU));
+#if defined(OS_WIN)
+ SetIcon(GetIndexOfCommandId(IDC_VIEW_INCOMPATIBILITIES),
+ *rb.GetBitmapNamed(IDR_CONFLICT_MENU));
+#endif
+
+ AddItemWithStringId(IDC_HELP_PAGE, IDS_HELP_PAGE);
+ if (browser_defaults::kShowExitMenuItem) {
+ AddSeparator();
+#if defined(OS_CHROMEOS)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession)) {
+ AddItemWithStringId(IDC_EXIT, IDS_EXIT_GUEST_MODE);
+ } else {
+ AddItemWithStringId(IDC_EXIT, IDS_SIGN_OUT);
+ }
+#else
+ AddItemWithStringId(IDC_EXIT, IDS_EXIT);
+#endif
+ }
+}
+
+void WrenchMenuModel::CreateCutCopyPaste() {
+ // WARNING: views/wrench_menu assumes these items are added in this order. If
+ // you change the order you'll need to update wrench_menu as well.
+ AddItemWithStringId(IDC_CUT, IDS_CUT);
+ AddItemWithStringId(IDC_COPY, IDS_COPY);
+ AddItemWithStringId(IDC_PASTE, IDS_PASTE);
+}
+
+void WrenchMenuModel::CreateZoomFullscreen() {
+ // WARNING: views/wrench_menu assumes these items are added in this order. If
+ // you change the order you'll need to update wrench_menu as well.
+ AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS);
+ AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS);
+ AddItemWithStringId(IDC_FULLSCREEN, IDS_FULLSCREEN);
+}
+
+void WrenchMenuModel::UpdateZoomControls() {
+ bool enable_increment = false;
+ bool enable_decrement = false;
+ int zoom_percent = 100;
+ if (browser_->GetSelectedTabContents()) {
+ zoom_percent = browser_->GetSelectedTabContents()->GetZoomPercent(
+ &enable_increment, &enable_decrement);
+ }
+ zoom_label_ = l10n_util::GetStringFUTF16(
+ IDS_ZOOM_PERCENT, base::IntToString16(zoom_percent));
+}
+
+string16 WrenchMenuModel::GetSyncMenuLabel() const {
+ return sync_ui_util::GetSyncMenuLabel(
+ browser_->profile()->GetOriginalProfile()->GetProfileSyncService());
+}
diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.h b/chrome/browser/ui/toolbar/wrench_menu_model.h
new file mode 100644
index 0000000..2690ea9
--- /dev/null
+++ b/chrome/browser/ui/toolbar/wrench_menu_model.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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_WRENCH_MENU_MODEL_H_
+#define CHROME_BROWSER_UI_TOOLBAR_WRENCH_MENU_MODEL_H_
+#pragma once
+
+#include "app/menus/accelerator.h"
+#include "app/menus/button_menu_item_model.h"
+#include "app/menus/simple_menu_model.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/tabs/tab_strip_model_observer.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+class Browser;
+class TabStripModel;
+
+namespace {
+class MockWrenchMenuModel;
+} // namespace
+
+// A menu model that builds the contents of an encoding menu.
+class EncodingMenuModel : public menus::SimpleMenuModel,
+ public menus::SimpleMenuModel::Delegate {
+ public:
+ explicit EncodingMenuModel(Browser* browser);
+ virtual ~EncodingMenuModel();
+
+ // Overridden from menus::SimpleMenuModel::Delegate:
+ virtual bool IsCommandIdChecked(int command_id) const;
+ virtual bool IsCommandIdEnabled(int command_id) const;
+ virtual bool GetAcceleratorForCommandId(int command_id,
+ menus::Accelerator* accelerator);
+ virtual void ExecuteCommand(int command_id);
+
+ private:
+ void Build();
+
+ Browser* browser_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(EncodingMenuModel);
+};
+
+// A menu model that builds the contents of the zoom menu.
+class ZoomMenuModel : public menus::SimpleMenuModel {
+ public:
+ explicit ZoomMenuModel(menus::SimpleMenuModel::Delegate* delegate);
+ virtual ~ZoomMenuModel();
+
+ private:
+ void Build();
+
+ DISALLOW_COPY_AND_ASSIGN(ZoomMenuModel);
+};
+
+class ToolsMenuModel : public menus::SimpleMenuModel {
+ public:
+ ToolsMenuModel(menus::SimpleMenuModel::Delegate* delegate, Browser* browser);
+ virtual ~ToolsMenuModel();
+
+ private:
+ void Build(Browser* browser);
+
+ scoped_ptr<EncodingMenuModel> encoding_menu_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(ToolsMenuModel);
+};
+
+// A menu model that builds the contents of the wrench menu.
+class WrenchMenuModel : public menus::SimpleMenuModel,
+ public menus::SimpleMenuModel::Delegate,
+ public menus::ButtonMenuItemModel::Delegate,
+ public TabStripModelObserver,
+ public NotificationObserver {
+ public:
+ WrenchMenuModel(menus::AcceleratorProvider* provider, Browser* browser);
+ virtual ~WrenchMenuModel();
+
+ // Overridden for ButtonMenuItemModel::Delegate:
+ virtual bool DoesCommandIdDismissMenu(int command_id) const;
+
+ // Overridden for both ButtonMenuItemModel::Delegate and SimpleMenuModel:
+ virtual bool IsItemForCommandIdDynamic(int command_id) const;
+ virtual string16 GetLabelForCommandId(int command_id) const;
+ virtual bool GetIconForCommandId(int command_id, SkBitmap* icon) const;
+ virtual void ExecuteCommand(int command_id);
+ virtual bool IsCommandIdChecked(int command_id) const;
+ virtual bool IsCommandIdEnabled(int command_id) const;
+ virtual bool IsCommandIdVisible(int command_id) const;
+ virtual bool GetAcceleratorForCommandId(
+ int command_id,
+ menus::Accelerator* accelerator);
+
+ // Overridden from TabStripModelObserver:
+ virtual void TabSelectedAt(TabContentsWrapper* old_contents,
+ TabContentsWrapper* new_contents,
+ int index,
+ bool user_gesture);
+ virtual void TabReplacedAt(TabContentsWrapper* old_contents,
+ TabContentsWrapper* new_contents, int index);
+ virtual void TabStripModelDeleted();
+
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Getters.
+ Browser* browser() const { return browser_; }
+
+ // Calculates |zoom_label_| in response to a zoom change.
+ void UpdateZoomControls();
+
+ private:
+ // Testing constructor used for mocking.
+ friend class ::MockWrenchMenuModel;
+ WrenchMenuModel();
+
+ void Build();
+
+ // Adds custom items to the menu. Deprecated in favor of a cross platform
+ // model for button items.
+ void CreateCutCopyPaste();
+ void CreateZoomFullscreen();
+
+ string16 GetSyncMenuLabel() const;
+
+ // Models for the special menu items with buttons.
+ scoped_ptr<menus::ButtonMenuItemModel> edit_menu_item_model_;
+ scoped_ptr<menus::ButtonMenuItemModel> zoom_menu_item_model_;
+
+ // Label of the zoom label in the zoom menu item.
+ string16 zoom_label_;
+
+ // Tools menu.
+ scoped_ptr<ToolsMenuModel> tools_menu_model_;
+
+ menus::AcceleratorProvider* provider_; // weak
+
+ Browser* browser_; // weak
+ TabStripModel* tabstrip_model_; // weak
+
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(WrenchMenuModel);
+};
+
+#endif // CHROME_BROWSER_UI_TOOLBAR_WRENCH_MENU_MODEL_H_
diff --git a/chrome/browser/ui/toolbar/wrench_menu_model_unittest.cc b/chrome/browser/ui/toolbar/wrench_menu_model_unittest.cc
new file mode 100644
index 0000000..92a8125
--- /dev/null
+++ b/chrome/browser/ui/toolbar/wrench_menu_model_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/wrench_menu_model.h"
+
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/test/browser_with_test_window_test.h"
+#include "chrome/test/menu_model_test.h"
+#include "chrome/test/testing_profile.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class WrenchMenuModelTest : public BrowserWithTestWindowTest,
+ public menus::AcceleratorProvider {
+ public:
+ // Don't handle accelerators.
+ virtual bool GetAcceleratorForCommandId(
+ int command_id,
+ menus::Accelerator* accelerator) { return false; }
+};
+
+// Copies parts of MenuModelTest::Delegate and combines them with the
+// WrenchMenuModel since WrenchMenuModel is now a SimpleMenuModel::Delegate and
+// not derived from SimpleMenuModel.
+class TestWrenchMenuModel : public WrenchMenuModel {
+ public:
+ TestWrenchMenuModel(menus::AcceleratorProvider* provider,
+ Browser* browser)
+ : WrenchMenuModel(provider, browser),
+ execute_count_(0),
+ checked_count_(0),
+ enable_count_(0) {
+ }
+
+ // Testing overrides to menus::SimpleMenuModel::Delegate:
+ virtual bool IsCommandIdChecked(int command_id) const {
+ bool val = WrenchMenuModel::IsCommandIdChecked(command_id);
+ if (val)
+ checked_count_++;
+ return val;
+ }
+
+ virtual bool IsCommandIdEnabled(int command_id) const {
+ ++enable_count_;
+ return true;
+ }
+
+ virtual void ExecuteCommand(int command_id) { ++execute_count_; }
+
+ int execute_count_;
+ mutable int checked_count_;
+ mutable int enable_count_;
+};
+
+TEST_F(WrenchMenuModelTest, Basics) {
+ TestWrenchMenuModel model(this, browser());
+ int itemCount = model.GetItemCount();
+
+ // Verify it has items. The number varies by platform, so we don't check
+ // the exact number.
+ EXPECT_GT(itemCount, 10);
+
+ // Execute a couple of the items and make sure it gets back to our delegate.
+ // We can't use CountEnabledExecutable() here because the encoding menu's
+ // delegate is internal, it doesn't use the one we pass in.
+ model.ActivatedAt(0);
+ EXPECT_TRUE(model.IsEnabledAt(0));
+ // Make sure to use the index that is not separator in all configurations.
+ model.ActivatedAt(2);
+ EXPECT_TRUE(model.IsEnabledAt(2));
+ EXPECT_EQ(model.execute_count_, 2);
+ EXPECT_EQ(model.enable_count_, 2);
+
+ model.execute_count_ = 0;
+ model.enable_count_ = 0;
+
+ // Choose something from the tools submenu and make sure it makes it back to
+ // the delegate as well. Use the first submenu as the tools one.
+ int toolsModelIndex = -1;
+ for (int i = 0; i < itemCount; ++i) {
+ if (model.GetTypeAt(i) == menus::MenuModel::TYPE_SUBMENU) {
+ toolsModelIndex = i;
+ break;
+ }
+ }
+ EXPECT_GT(toolsModelIndex, -1);
+ menus::MenuModel* toolsModel = model.GetSubmenuModelAt(toolsModelIndex);
+ EXPECT_TRUE(toolsModel);
+ EXPECT_GT(toolsModel->GetItemCount(), 2);
+ toolsModel->ActivatedAt(2);
+ EXPECT_TRUE(toolsModel->IsEnabledAt(2));
+ EXPECT_EQ(model.execute_count_, 1);
+ EXPECT_EQ(model.enable_count_, 1);
+}
+
+class EncodingMenuModelTest : public BrowserWithTestWindowTest,
+ public MenuModelTest {
+};
+
+TEST_F(EncodingMenuModelTest, IsCommandIdCheckedWithNoTabs) {
+ EncodingMenuModel model(browser());
+ ASSERT_EQ(NULL, browser()->GetSelectedTabContents());
+ EXPECT_FALSE(model.IsCommandIdChecked(IDC_ENCODING_ISO88591));
+}