summaryrefslogtreecommitdiffstats
path: root/chrome/browser/tabs
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-02 18:11:09 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-02 18:11:09 +0000
commitbfe4c158cd0372fa7a602997f65b42a92645aaf3 (patch)
treedbda26fa776eb9c3b5fcca2e1b1fa956db1985e8 /chrome/browser/tabs
parent5fa6d7fa100a6836cc7b791a094468f8c384807b (diff)
downloadchromium_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.cc112
-rw-r--r--chrome/browser/tabs/tab_strip_model.h21
-rw-r--r--chrome/browser/tabs/tab_strip_model_unittest.cc75
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());
+ }
+}