diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-02 18:11:09 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-02 18:11:09 +0000 |
commit | bfe4c158cd0372fa7a602997f65b42a92645aaf3 (patch) | |
tree | dbda26fa776eb9c3b5fcca2e1b1fa956db1985e8 /chrome/browser/tabs | |
parent | 5fa6d7fa100a6836cc7b791a094468f8c384807b (diff) | |
download | chromium_src-bfe4c158cd0372fa7a602997f65b42a92645aaf3.zip chromium_src-bfe4c158cd0372fa7a602997f65b42a92645aaf3.tar.gz chromium_src-bfe4c158cd0372fa7a602997f65b42a92645aaf3.tar.bz2 |
Any time we are shutting down a tab, try to use fast shutdown.
BUG=http://crbug.com/5638
TEST=existing tab strip model tests, Fast shutdown ui tests, new tab strip model fast shutdown test
Review URL: http://codereview.chromium.org/235050
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27865 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/tabs')
-rw-r--r-- | chrome/browser/tabs/tab_strip_model.cc | 112 | ||||
-rw-r--r-- | chrome/browser/tabs/tab_strip_model.h | 21 | ||||
-rw-r--r-- | chrome/browser/tabs/tab_strip_model_unittest.cc | 75 |
3 files changed, 160 insertions, 48 deletions
diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc index baca9b0..b356c62 100644 --- a/chrome/browser/tabs/tab_strip_model.cc +++ b/chrome/browser/tabs/tab_strip_model.cc @@ -10,6 +10,7 @@ #include "base/stl_util-inl.h" #include "base/string_util.h" #include "build/build_config.h" +#include "chrome/browser/browser_shutdown.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/profile.h" #include "chrome/browser/sessions/tab_restore_service.h" @@ -122,7 +123,9 @@ void TabStripModel::ReplaceNavigationControllerAt( // occurs between the call to add an aditional tab and one to close // the previous tab. InsertTabContentsAt(index + 1, controller->tab_contents(), true, true); - InternalCloseTabContentsAt(index, false); + std::vector<int> closing_tabs; + closing_tabs.push_back(index); + InternalCloseTabs(closing_tabs, false); } TabContents* TabStripModel::DetachTabContentsAt(int index) { @@ -209,8 +212,16 @@ void TabStripModel::CloseAllTabs() { // specific condition when CloseTabContentsAt causes a flurry of // Close/Detach/Select notifications to be sent. closing_all_ = true; + std::vector<int> closing_tabs; for (int i = count() - 1; i >= 0; --i) - CloseTabContentsAt(i); + closing_tabs.push_back(i); + InternalCloseTabs(closing_tabs, true); +} + +bool TabStripModel::CloseTabContentsAt(int index) { + std::vector<int> closing_tabs; + closing_tabs.push_back(index); + return InternalCloseTabs(closing_tabs, true); } bool TabStripModel::TabsAreLoading() const { @@ -504,28 +515,27 @@ void TabStripModel::ExecuteContextMenuCommand( case CommandCloseOtherTabs: { UserMetrics::RecordAction(L"TabContextMenu_CloseOtherTabs", profile_); TabContents* contents = GetTabContentsAt(context_index); + std::vector<int> closing_tabs; for (int i = count() - 1; i >= 0; --i) { if (GetTabContentsAt(i) != contents) - CloseTabContentsAt(i); + closing_tabs.push_back(i); } + InternalCloseTabs(closing_tabs, true); break; } case CommandCloseTabsToRight: { UserMetrics::RecordAction(L"TabContextMenu_CloseTabsToRight", profile_); - for (int i = count() - 1; i > context_index; --i) - CloseTabContentsAt(i); + std::vector<int> closing_tabs; + for (int i = count() - 1; i > context_index; --i) { + closing_tabs.push_back(i); + } + InternalCloseTabs(closing_tabs, true); break; } case CommandCloseTabsOpenedBy: { UserMetrics::RecordAction(L"TabContextMenu_CloseTabsOpenedBy", profile_); - NavigationController* opener = - &GetTabContentsAt(context_index)->controller(); - - for (int i = count() - 1; i >= 0; --i) { - if (OpenerMatches(contents_data_.at(i), opener, true)) - CloseTabContentsAt(i); - } - + std::vector<int> closing_tabs = GetIndexesOpenedBy(context_index); + InternalCloseTabs(closing_tabs, true); break; } case CommandRestoreTab: { @@ -583,34 +593,68 @@ bool TabStripModel::IsNewTabAtEndOfTabStrip(TabContents* contents) const { contents->controller().entry_count() == 1; } -bool TabStripModel::InternalCloseTabContentsAt(int index, - bool create_historical_tab) { - if (!delegate_->CanCloseContentsAt(index)) - return false; +bool TabStripModel::InternalCloseTabs(std::vector<int> indices, + bool create_historical_tabs) { + bool retval = true; + + // We only try the fast shutdown path if the whole browser process is *not* + // shutting down. Fast shutdown during browser termination is handled in + // BrowserShutdown. + if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID) { + // Construct a map of processes to the number of associated tabs that are + // closing. + std::map<RenderProcessHost*, size_t> processes; + for (size_t i = 0; i < indices.size(); ++i) { + if (!delegate_->CanCloseContentsAt(indices[i])) { + retval = false; + continue; + } + + TabContents* detached_contents = GetContentsAt(indices[i]); + RenderProcessHost* process = detached_contents->process(); + std::map<RenderProcessHost*, size_t>::iterator iter = + processes.find(process); + if (iter == processes.end()) { + processes[process] = 1; + } else { + iter->second++; + } + } - TabContents* detached_contents = GetContentsAt(index); + // Try to fast shutdown the tabs that can close. + for (std::map<RenderProcessHost*, size_t>::iterator iter = + processes.begin(); + iter != processes.end(); ++iter) { + iter->first->FastShutdownForPageCount(iter->second); + } + } - if (delegate_->RunUnloadListenerBeforeClosing(detached_contents)) - return false; + // We now return to our regularly scheduled shutdown procedure. + for (size_t i = 0; i < indices.size(); ++i) { + TabContents* detached_contents = GetContentsAt(indices[i]); - // TODO: Now that we know the tab has no unload/beforeunload listeners, - // we should be able to do a fast shutdown of the RenderViewProcess. - // Make sure that we actually do. + if (!delegate_->CanCloseContentsAt(indices[i]) || + delegate_->RunUnloadListenerBeforeClosing(detached_contents)) { + retval = false; + continue; + } - FOR_EACH_OBSERVER(TabStripModelObserver, observers_, - TabClosingAt(detached_contents, index)); + FOR_EACH_OBSERVER(TabStripModelObserver, observers_, + TabClosingAt(detached_contents, indices[i])); - if (detached_contents) { - // Ask the delegate to save an entry for this tab in the historical tab - // database if applicable. - if (create_historical_tab) - delegate_->CreateHistoricalTab(detached_contents); + if (detached_contents) { + // Ask the delegate to save an entry for this tab in the historical tab + // database if applicable. + if (create_historical_tabs) + delegate_->CreateHistoricalTab(detached_contents); - // Deleting the TabContents will call back to us via NotificationObserver - // and detach it. - delete detached_contents; + // Deleting the TabContents will call back to us via NotificationObserver + // and detach it. + delete detached_contents; + } } - return true; + + return retval; } void TabStripModel::MoveTabContentsAtImpl(int index, int to_position, diff --git a/chrome/browser/tabs/tab_strip_model.h b/chrome/browser/tabs/tab_strip_model.h index b75a9f86..cbf23a7 100644 --- a/chrome/browser/tabs/tab_strip_model.h +++ b/chrome/browser/tabs/tab_strip_model.h @@ -296,9 +296,7 @@ class TabStripModel : public NotificationObserver { // Returns true if the TabContents was closed immediately, false if it was not // closed (we may be waiting for a response from an onunload handler, or // waiting for the user to confirm closure). - bool CloseTabContentsAt(int index) { - return InternalCloseTabContentsAt(index, true); - } + bool CloseTabContentsAt(int index); // Replaces the entire state of a the tab at index by switching in a // different NavigationController. This is used through the recently @@ -498,19 +496,20 @@ class TabStripModel : public NotificationObserver { // something related to their current activity. bool IsNewTabAtEndOfTabStrip(TabContents* contents) const; - // Closes the TabContents at the specified index. This causes the TabContents - // to be destroyed, but it may not happen immediately (e.g. if it's a - // TabContents). If the page in question has an unload event the TabContents - // will not be destroyed until after the event has completed, which will then - // call back into this method. + // Closes the TabContents at the specified indices. This causes the + // TabContents to be destroyed, but it may not happen immediately. + // If the page in question has an unload event the + // TabContents will not be destroyed until after the event has completed, + // which will then call back into this method. // // The boolean parameter create_historical_tab controls whether to - // record this tab and its history for reopening recently closed + // record these tabs and their history for reopening recently closed // tabs. // - // Returns true if the TabContents was closed immediately, false if we are + // Returns true if the TabContents were closed immediately, false if we are // waiting for the result of an onunload handler. - bool InternalCloseTabContentsAt(int index, bool create_historical_tab); + bool InternalCloseTabs(std::vector<int> indices, + bool create_historical_tabs); void MoveTabContentsAtImpl(int index, int to_position, bool select_after_move, diff --git a/chrome/browser/tabs/tab_strip_model_unittest.cc b/chrome/browser/tabs/tab_strip_model_unittest.cc index bc2eaa8..585fec5 100644 --- a/chrome/browser/tabs/tab_strip_model_unittest.cc +++ b/chrome/browser/tabs/tab_strip_model_unittest.cc @@ -23,10 +23,11 @@ class TabStripDummyDelegate : public TabStripModelDelegate { public: explicit TabStripDummyDelegate(TabContents* dummy) - : dummy_contents_(dummy), can_close_(true) {} + : dummy_contents_(dummy), can_close_(true), run_unload_(false) {} virtual ~TabStripDummyDelegate() {} void set_can_close(bool value) { can_close_ = value; } + void set_run_unload_listener(bool value) { run_unload_ = value; } // Overridden from TabStripModelDelegate: virtual TabContents* AddBlankTab(bool foreground) { return NULL; } @@ -59,7 +60,7 @@ class TabStripDummyDelegate : public TabStripModelDelegate { virtual void CloseFrameAfterDragSession() {} virtual void CreateHistoricalTab(TabContents* contents) {} virtual bool RunUnloadListenerBeforeClosing(TabContents* contents) { - return false; + return run_unload_; } virtual bool CanRestoreTab() { return false; } virtual void RestoreTab() {} @@ -73,7 +74,10 @@ class TabStripDummyDelegate : public TabStripModelDelegate { // Whether tabs can be closed. bool can_close_; - DISALLOW_EVIL_CONSTRUCTORS(TabStripDummyDelegate); + // Whether to report that we need to run an unload listener before closing. + bool run_unload_; + + DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate); }; class TabStripModelTest : public RenderViewHostTestHarness { @@ -82,6 +86,14 @@ class TabStripModelTest : public RenderViewHostTestHarness { return new TabContents(profile(), NULL, 0, NULL); } + TabContents* CreateTabContentsWithSharedRPH(TabContents* tab_contents) { + TabContents* retval = new TabContents(profile(), + tab_contents->render_view_host()->site_instance(), MSG_ROUTING_NONE, + NULL); + EXPECT_EQ(retval->process(), tab_contents->process()); + return retval; + } + // Forwards a URL "load" request through to our dummy TabContents // implementation. void LoadURL(TabContents* con, const std::wstring& url) { @@ -1464,3 +1476,60 @@ TEST_F(TabStripModelTest, Pinning) { tabstrip.CloseAllTabs(); } + +// Tests that fast shutdown is attempted appropriately. +TEST_F(TabStripModelTest, FastShutdown) { + TabStripDummyDelegate delegate(NULL); + TabStripModel tabstrip(&delegate, profile()); + MockTabStripModelObserver observer; + tabstrip.AddObserver(&observer); + + EXPECT_TRUE(tabstrip.empty()); + + // Make sure fast shutdown is attempted when tabs that share a RPH are shut + // down. + { + TabContents* contents1 = CreateTabContents(); + TabContents* contents2 = CreateTabContentsWithSharedRPH(contents1); + + SetID(contents1, 1); + SetID(contents2, 2); + + tabstrip.AppendTabContents(contents1, true); + tabstrip.AppendTabContents(contents2, true); + + // Turn on the fake unload listener so the tabs don't actually get shut + // down when we call CloseAllTabs()---we need to be able to check that + // fast shutdown was attempted. + delegate.set_run_unload_listener(true); + tabstrip.CloseAllTabs(); + // On a mock RPH this checks whether we *attempted* fast shutdown. + // A real RPH would reject our attempt since there is an unload handler. + EXPECT_TRUE(contents1->process()->fast_shutdown_started()); + EXPECT_EQ(2, tabstrip.count()); + + delegate.set_run_unload_listener(false); + tabstrip.CloseAllTabs(); + EXPECT_TRUE(tabstrip.empty()); + } + + // Make sure fast shutdown is not attempted when only some tabs that share a + // RPH are shut down. + { + TabContents* contents1 = CreateTabContents(); + TabContents* contents2 = CreateTabContentsWithSharedRPH(contents1); + + SetID(contents1, 1); + SetID(contents2, 2); + + tabstrip.AppendTabContents(contents1, true); + tabstrip.AppendTabContents(contents2, true); + + tabstrip.CloseTabContentsAt(1); + EXPECT_FALSE(contents1->process()->fast_shutdown_started()); + EXPECT_EQ(1, tabstrip.count()); + + tabstrip.CloseAllTabs(); + EXPECT_TRUE(tabstrip.empty()); + } +} |