Skip to content

Commit

Permalink
Merge pull request #3836 from brave/tab_menu_model
Browse files Browse the repository at this point in the history
Restore deleted tab context menu
  • Loading branch information
bsclifton authored Nov 4, 2019
2 parents 4de8e57 + feff2ae commit 7723f74
Show file tree
Hide file tree
Showing 10 changed files with 458 additions and 0 deletions.
16 changes: 16 additions & 0 deletions app/brave_generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,22 @@ By installing this extension, you are agreeing to the Google Widevine Terms of U
<message name="IDS_BRAVE_CRYPTO_WALLETS_SETUP" desc="Use Crypto Wallets">
SETUP
</message>
<if expr="not use_titlecase">
<message name="IDS_TAB_CXMENU_CLOSEOTHERTABS" desc="The label of the 'Close other tabs' Tab context menu item.">
Close other tabs
</message>
<message name="IDS_TAB_CXMENU_BOOKMARK_ALL_TABS" desc="The label of the tab context menu item for creating a bookmark folder containing an entry for each open tab.">
Bookmark all tabs...
</message>
</if>
<if expr="use_titlecase">
<message name="IDS_TAB_CXMENU_CLOSEOTHERTABS" desc="In Title Case: The label of the 'Close Other Tabs' Tab context menu item.">
Close Other Tabs
</message>
<message name="IDS_TAB_CXMENU_BOOKMARK_ALL_TABS" desc="In Title Case: The label of the tab context menu item for creating a bookmark folder containing an entry for each open tab.">
Bookmark All Tabs...
</message>
</if>
</messages>
<includes>
<include name="IDR_BRAVE_GOOGLE_ANALYTICS_POLYFILL" file="resources/js/google_analytics_polyfill.js" type="BINDATA" />
Expand Down
5 changes: 5 additions & 0 deletions browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ source_set("ui") {
"content_settings/brave_content_setting_image_models.h",
"omnibox/brave_omnibox_client.cc",
"omnibox/brave_omnibox_client.h",
"tabs/brave_tab_menu_model.cc",
"tabs/brave_tab_menu_model.h",
"tabs/tab_menu_model_delegate_proxy.cc",
"tabs/tab_menu_model_delegate_proxy.h",
"toolbar/brave_app_menu_model.cc",
"toolbar/brave_app_menu_model.h",
"webui/brave_settings_ui.cc",
Expand Down Expand Up @@ -166,6 +170,7 @@ source_set("ui") {
"//chrome/common",
"//components/gcm_driver:gcm_buildflags",
"//components/prefs",
"//components/sessions",
"//content/public/browser",
"//content/public/common",
"//skia",
Expand Down
64 changes: 64 additions & 0 deletions browser/ui/tabs/brave_tab_menu_model.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/browser/ui/tabs/brave_tab_menu_model.h"

#include "brave/browser/ui/tabs/tab_menu_model_delegate_proxy.h"
#include "chrome/browser/sessions/tab_restore_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/grit/generated_resources.h"
#include "components/sessions/core/tab_restore_service.h"

BraveTabMenuModel::BraveTabMenuModel(ui::SimpleMenuModel::Delegate* delegate,
TabStripModel* tab_strip_model,
int index)
: TabMenuModel(new TabMenuModelDelegateProxy(delegate,
tab_strip_model,
index),
tab_strip_model,
index),
index_(index),
tab_strip_model_(tab_strip_model) {
// Take owner ship of TabMenuModelDelegateProxy instance.
// There is no way to instantiate it before calling ctor of base class.
delegate_proxy_.reset(TabMenuModel::delegate());
Build();
}

BraveTabMenuModel::~BraveTabMenuModel() {
}

int BraveTabMenuModel::GetRestoreTabCommandStringId() const {
int id = IDS_RESTORE_TAB;

content::WebContents* tab = tab_strip_model_->GetWebContentsAt(index_);
if (!tab)
return id;

auto* browser = chrome::FindBrowserWithWebContents(tab);
auto* restore_service =
TabRestoreServiceFactory::GetForProfile(browser->profile());
if (!restore_service)
return id;

if (!restore_service->IsLoaded() || restore_service->entries().empty())
return id;

if (restore_service->entries().front()->type ==
sessions::TabRestoreService::WINDOW)
id = IDS_RESTORE_WINDOW;

return id;
}

void BraveTabMenuModel::Build() {
AddItemWithStringId(CommandCloseOtherTabs, IDS_TAB_CXMENU_CLOSEOTHERTABS);
AddSeparator(ui::NORMAL_SEPARATOR);
AddItemWithStringId(CommandRestoreTab, GetRestoreTabCommandStringId());
AddItemWithStringId(CommandBookmarkAllTabs, IDS_TAB_CXMENU_BOOKMARK_ALL_TABS);
}
43 changes: 43 additions & 0 deletions browser/ui/tabs/brave_tab_menu_model.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_UI_TABS_BRAVE_TAB_MENU_MODEL_H_
#define BRAVE_BROWSER_UI_TABS_BRAVE_TAB_MENU_MODEL_H_

#include <memory>

#include "chrome/browser/ui/tabs/tab_menu_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"

class BraveTabMenuModel : public TabMenuModel {
public:
enum BraveTabContextMenuCommand {
CommandStart = TabStripModel::CommandLast,
CommandCloseOtherTabs,
CommandRestoreTab,
CommandBookmarkAllTabs,
CommandLast,
};

BraveTabMenuModel(ui::SimpleMenuModel::Delegate* delegate,
TabStripModel* tab_strip_model,
int index);
~BraveTabMenuModel() override;

private:
FRIEND_TEST_ALL_PREFIXES(BraveTabMenuModelTest, Basics);

void Build();

int GetRestoreTabCommandStringId() const;

int index_;
TabStripModel* tab_strip_model_;
std::unique_ptr<ui::SimpleMenuModel::Delegate> delegate_proxy_;

DISALLOW_COPY_AND_ASSIGN(BraveTabMenuModel);
};

#endif // BRAVE_BROWSER_UI_TABS_BRAVE_TAB_MENU_MODEL_H_
124 changes: 124 additions & 0 deletions browser/ui/tabs/brave_tab_menu_model_browsertest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/browser/ui/tabs/brave_tab_menu_model.h"

#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/models/simple_menu_model.h"

using BraveTabMenuModelTest = InProcessBrowserTest;

class Delegate : public ui::SimpleMenuModel::Delegate {
public:
Delegate() : execute_count_(0), enable_count_(0) {}

bool IsCommandIdChecked(int command_id) const override {
return false;
}
bool IsCommandIdEnabled(int command_id) const override {
++enable_count_;
return true;
}

void ExecuteCommand(int command_id, int event_flags) override {
++execute_count_;
}

int execute_count_;
mutable int enable_count_;
};

// Recursively checks the enabled state and executes a command on every item
// that's not a separator or a submenu parent item. The returned count should
// match the number of times the delegate is called to ensure every item works.
void CountEnabledExecutable(ui::MenuModel* model,
int* count) {
for (int i = 0; i < model->GetItemCount(); ++i) {
ui::MenuModel::ItemType type = model->GetTypeAt(i);
switch (type) {
case ui::MenuModel::TYPE_SEPARATOR:
continue;
case ui::MenuModel::TYPE_SUBMENU:
CountEnabledExecutable(model->GetSubmenuModelAt(i), count);
break;
case ui::MenuModel::TYPE_COMMAND:
case ui::MenuModel::TYPE_CHECK:
case ui::MenuModel::TYPE_RADIO:
model->IsEnabledAt(i); // Check if it's enabled (ignore answer).
model->ActivatedAt(i); // Execute it.
(*count)++; // Increment the count of executable items seen.
break;
default:
FAIL(); // Ensure every case is tested.
}
}
}

IN_PROC_BROWSER_TEST_F(BraveTabMenuModelTest, Basics) {
Delegate delegate;
BraveTabMenuModel model(&delegate, browser()->tab_strip_model(), 0);

// Verify it has items. The number varies by platform, so we don't check
// the exact number.
// Chromium uses 5 but we added three more items. So, use 8.
const int additional_brave_items = 3;
EXPECT_GT(model.GetItemCount(), 5 + additional_brave_items);

int item_count = 0;
CountEnabledExecutable(&model, &item_count);

// Brave added three more items and it is not countable by |delegate_|
// because proxy handles them instead of passing |delegate_|.
// So, plus 3 to chromium's tab menu count.
EXPECT_GT(item_count, 0);
EXPECT_EQ(item_count, delegate.execute_count_ + additional_brave_items);
EXPECT_EQ(item_count, delegate.enable_count_ + additional_brave_items);

// All items are disable state when there is only one tab.
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandCloseOtherTabs));
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandRestoreTab));
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandBookmarkAllTabs));

chrome::NewTab(browser());
// Still close other tabs is disabled because currently running context menu
// is for tab at zero and it's not selected tab. If other tab is only one and
// selected, close other tabs item is not enabled.
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandCloseOtherTabs));
// Still restore tab menu is disabled because there is no closed tab.
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandRestoreTab));
// Bookmark all tabs item is enabled if the number of tabs are 2 or more.
EXPECT_TRUE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandBookmarkAllTabs));

// If the other tab is un-selected tab, it can be closed by close other tabs
// menu.
browser()->tab_strip_model()->ActivateTabAt(0);
EXPECT_TRUE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandCloseOtherTabs));

// If the other tab is pinned tab, close other tabs is disabled.
browser()->tab_strip_model()->SetTabPinned(1, true);
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandCloseOtherTabs));

browser()->tab_strip_model()->SetTabPinned(1, false);

chrome::CloseTab(browser());
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandCloseOtherTabs));
// When a tab is closed, restore tab menu item is enabled.
EXPECT_TRUE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandRestoreTab));
EXPECT_FALSE(model.delegate()->IsCommandIdEnabled(
BraveTabMenuModel::CommandBookmarkAllTabs));
}
Loading

0 comments on commit 7723f74

Please sign in to comment.