Commit 419139b3 authored by Yuheng Huang's avatar Yuheng Huang Committed by Commit Bot

Tab Search: Implement TabsRemoved API

This change makes close tab more efficient. According to the performance
benchmark https://pinpoint-dot-chromeperf.appspot.com/job/15a9cbdd520000
It reduces close tab latency from 105ms to 37ms.

Bug: 1099917
Change-Id: I987a98006d5b8788af8ab844a8871458a620b20c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2527625
Commit-Queue: Yuheng Huang <yuhengh@chromium.org>
Reviewed-by: default avatarThomas Lukaszewicz <tluk@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#826147}
parent 5c07458a
...@@ -137,7 +137,9 @@ export class TabSearchAppElement extends PolymerElement { ...@@ -137,7 +137,9 @@ export class TabSearchAppElement extends PolymerElement {
const callbackRouter = this.apiProxy_.getCallbackRouter(); const callbackRouter = this.apiProxy_.getCallbackRouter();
this.listenerIds_.push( this.listenerIds_.push(
callbackRouter.tabsChanged.addListener(() => this.updateTabs_()), callbackRouter.tabsChanged.addListener(() => this.updateTabs_()),
callbackRouter.tabUpdated.addListener(tab => this.onTabUpdated_(tab))); callbackRouter.tabUpdated.addListener(tab => this.onTabUpdated_(tab)),
callbackRouter.tabsRemoved.addListener(
tabIds => this.onTabsRemoved_(tabIds)));
this.updateTabs_(); this.updateTabs_();
} }
...@@ -206,6 +208,21 @@ export class TabSearchAppElement extends PolymerElement { ...@@ -206,6 +208,21 @@ export class TabSearchAppElement extends PolymerElement {
} }
} }
/**
* @param {!Array<number>} tabIds
* @private
*/
onTabsRemoved_(tabIds) {
const windows = this.openTabs_;
if (windows) {
const ids = new Set(tabIds);
for (const window of windows) {
window.tabs = window.tabs.filter(tab => (!ids.has(tab.tabId)));
}
this.openTabs_ = windows.concat();
}
}
/** /**
* The seleted item's index, or -1 if no item selected. * The seleted item's index, or -1 if no item selected.
* @return {number} * @return {number}
......
...@@ -105,10 +105,13 @@ interface PageHandler { ...@@ -105,10 +105,13 @@ interface PageHandler {
// WebUI-side handler for requests from the browser. // WebUI-side handler for requests from the browser.
interface Page { interface Page {
// Callback when any tabs in the current profile are // Callback when any tabs in the current profile are
// selected/inserted/removed/moved/replaced // selected/inserted/moved/replaced
TabsChanged(); TabsChanged();
// Callback when a tab is updated within the same web contents. // Callback when a tab is updated within the same web contents.
// e.g. url, title or favicon changed. // e.g. url, title or favicon changed.
TabUpdated(Tab tab); TabUpdated(Tab tab);
// Callback when tabs are removed.
TabsRemoved(array<int32> tab_ids);
}; };
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/tabs/tab_renderer_data.h" #include "chrome/browser/ui/tabs/tab_renderer_data.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/webui/util/image_util.h" #include "chrome/browser/ui/webui/util/image_util.h"
namespace { namespace {
...@@ -207,8 +208,18 @@ void TabSearchPageHandler::OnTabStripModelChanged( ...@@ -207,8 +208,18 @@ void TabSearchPageHandler::OnTabStripModelChanged(
TabStripModel* tab_strip_model, TabStripModel* tab_strip_model,
const TabStripModelChange& change, const TabStripModelChange& change,
const TabStripSelectionChange& selection) { const TabStripSelectionChange& selection) {
if (!browser_tab_strip_tracker_.is_processing_initial_browsers()) if (browser_tab_strip_tracker_.is_processing_initial_browsers())
ScheduleDebounce(); return;
if (change.type() == TabStripModelChange::kRemoved) {
std::vector<int> tab_ids;
for (auto& content_with_index : change.GetRemove()->contents) {
tab_ids.push_back(
extensions::ExtensionTabUtil::GetTabId(content_with_index.contents));
}
page_->TabsRemoved(tab_ids);
return;
}
ScheduleDebounce();
} }
void TabSearchPageHandler::TabChangedAt(content::WebContents* contents, void TabSearchPageHandler::TabChangedAt(content::WebContents* contents,
......
...@@ -49,6 +49,7 @@ class MockPage : public tab_search::mojom::Page { ...@@ -49,6 +49,7 @@ class MockPage : public tab_search::mojom::Page {
MOCK_METHOD0(TabsChanged, void()); MOCK_METHOD0(TabsChanged, void());
MOCK_METHOD1(TabUpdated, void(tab_search::mojom::TabPtr)); MOCK_METHOD1(TabUpdated, void(tab_search::mojom::TabPtr));
MOCK_METHOD1(TabsRemoved, void(const std::vector<int32_t>& tab_ids));
}; };
void ExpectNewTab(const tab_search::mojom::Tab* tab, void ExpectNewTab(const tab_search::mojom::Tab* tab,
...@@ -191,6 +192,7 @@ TEST_F(TabSearchPageHandlerTest, GetTabs) { ...@@ -191,6 +192,7 @@ TEST_F(TabSearchPageHandlerTest, GetTabs) {
EXPECT_CALL(page_, TabsChanged()).Times(1); EXPECT_CALL(page_, TabsChanged()).Times(1);
EXPECT_CALL(page_, TabUpdated(_)).Times(2); EXPECT_CALL(page_, TabUpdated(_)).Times(2);
EXPECT_CALL(page_, TabsRemoved(_)).Times(2);
handler()->mock_debounce_timer()->Fire(); handler()->mock_debounce_timer()->Fire();
int32_t tab_id2 = 0; int32_t tab_id2 = 0;
...@@ -257,8 +259,9 @@ TEST_F(TabSearchPageHandlerTest, GetTabs) { ...@@ -257,8 +259,9 @@ TEST_F(TabSearchPageHandlerTest, GetTabs) {
// TabsChanged() and TabsChanged() is only called when the page handler's // TabsChanged() and TabsChanged() is only called when the page handler's
// timer fires. // timer fires.
TEST_F(TabSearchPageHandlerTest, TabsChanged) { TEST_F(TabSearchPageHandlerTest, TabsChanged) {
EXPECT_CALL(page_, TabsChanged()).Times(4); EXPECT_CALL(page_, TabsChanged()).Times(3);
EXPECT_CALL(page_, TabUpdated(_)).Times(1); EXPECT_CALL(page_, TabUpdated(_)).Times(1);
EXPECT_CALL(page_, TabsRemoved(_)).Times(3);
FireTimer(); // Will call TabsChanged(). FireTimer(); // Will call TabsChanged().
// Add 2 tabs in browser1. // Add 2 tabs in browser1.
...@@ -281,8 +284,7 @@ TEST_F(TabSearchPageHandlerTest, TabsChanged) { ...@@ -281,8 +284,7 @@ TEST_F(TabSearchPageHandlerTest, TabsChanged) {
ASSERT_FALSE(IsTimerRunning()); ASSERT_FALSE(IsTimerRunning());
browser1()->tab_strip_model()->CloseWebContentsAt( browser1()->tab_strip_model()->CloseWebContentsAt(
0, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); 0, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
ASSERT_TRUE(IsTimerRunning()); ASSERT_FALSE(IsTimerRunning());
FireTimer(); // Will call TabsChanged().
} }
// Ensure that tab model changes in a browser with a different profile // Ensure that tab model changes in a browser with a different profile
...@@ -309,6 +311,7 @@ bool VerifyTabUpdated(const tab_search::mojom::TabPtr& tab) { ...@@ -309,6 +311,7 @@ bool VerifyTabUpdated(const tab_search::mojom::TabPtr& tab) {
TEST_F(TabSearchPageHandlerTest, TabUpdated) { TEST_F(TabSearchPageHandlerTest, TabUpdated) {
EXPECT_CALL(page_, TabsChanged()).Times(1); EXPECT_CALL(page_, TabsChanged()).Times(1);
EXPECT_CALL(page_, TabUpdated(Truly(VerifyTabUpdated))).Times(1); EXPECT_CALL(page_, TabUpdated(Truly(VerifyTabUpdated))).Times(1);
EXPECT_CALL(page_, TabsRemoved(_)).Times(1);
AddTabWithTitle(browser1(), GURL(kTabUrl1), kTabName1); AddTabWithTitle(browser1(), GURL(kTabUrl1), kTabName1);
// Adding the following tab will trigger TabUpdated() to the first tab // Adding the following tab will trigger TabUpdated() to the first tab
// since the tab index will change from 0 to 1 // since the tab index will change from 0 to 1
...@@ -327,6 +330,7 @@ TEST_F(TabSearchPageHandlerTest, CloseTab) { ...@@ -327,6 +330,7 @@ TEST_F(TabSearchPageHandlerTest, CloseTab) {
browser2()->tab_strip_model()->GetWebContentsAt(0)); browser2()->tab_strip_model()->GetWebContentsAt(0));
EXPECT_CALL(page_, TabsChanged()).Times(1); EXPECT_CALL(page_, TabsChanged()).Times(1);
EXPECT_CALL(page_, TabUpdated(_)).Times(1); EXPECT_CALL(page_, TabUpdated(_)).Times(1);
EXPECT_CALL(page_, TabsRemoved(_)).Times(3);
handler()->CloseTab(tab_id); handler()->CloseTab(tab_id);
ASSERT_EQ(1, browser1()->tab_strip_model()->count()); ASSERT_EQ(1, browser1()->tab_strip_model()->count());
ASSERT_EQ(1, browser2()->tab_strip_model()->count()); ASSERT_EQ(1, browser2()->tab_strip_model()->count());
......
...@@ -229,6 +229,14 @@ suite('TabSearchAppTest', () => { ...@@ -229,6 +229,14 @@ suite('TabSearchAppTest', () => {
assertEquals('example.com', tabSearchItem.data.hostname); assertEquals('example.com', tabSearchItem.data.hostname);
}); });
test('refresh on tabs removed', async () => {
await setupTest(sampleData());
verifyTabIds(queryRows(), [1, 5, 6, 2, 3, 4]);
testProxy.getCallbackRouterRemote().tabsRemoved([1, 2]);
await flushTasks();
verifyTabIds(queryRows(), [5, 6, 3, 4]);
});
test('Verify initial tab render time is logged correctly', async () => { test('Verify initial tab render time is logged correctly', async () => {
// |metricNames| tracks thow many times recordTime() has been called for // |metricNames| tracks thow many times recordTime() has been called for
// a metric. // a metric.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment