summaryrefslogtreecommitdiffstats
path: root/chrome/browser
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
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')
-rw-r--r--chrome/browser/browser.cc1
-rw-r--r--chrome/browser/browser_shutdown.cc52
-rw-r--r--chrome/browser/browser_shutdown.h3
-rw-r--r--chrome/browser/external_tab_container.h1
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc13
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.h3
-rw-r--r--chrome/browser/renderer_host/mock_render_process_host.cc5
-rw-r--r--chrome/browser/renderer_host/render_process_host.cc7
-rw-r--r--chrome/browser/renderer_host/render_process_host.h14
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc2
-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
13 files changed, 220 insertions, 89 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 13288c9..e0cc047 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -509,7 +509,6 @@ void Browser::InProgressDownloadResponse(bool cancel_downloads) {
ShowDownloadsTab();
}
-
////////////////////////////////////////////////////////////////////////////////
// Browser, Tab adding/showing functions:
diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc
index 3e8622a..22f4507 100644
--- a/chrome/browser/browser_shutdown.cc
+++ b/chrome/browser/browser_shutdown.cc
@@ -52,34 +52,34 @@ void RegisterPrefs(PrefService* local_state) {
local_state->RegisterIntegerPref(prefs::kShutdownNumProcessesSlow, 0);
}
+ShutdownType GetShutdownType() {
+ return shutdown_type_;
+}
+
void OnShutdownStarting(ShutdownType type) {
- // TODO(erikkay): http://b/753080 when onbeforeunload is supported at
- // shutdown, fix this to allow these variables to be reset.
- if (shutdown_type_ == NOT_VALID) {
- shutdown_type_ = type;
- // For now, we're only counting the number of renderer processes
- // since we can't safely count the number of plugin processes from this
- // thread, and we'd really like to avoid anything which might add further
- // delays to shutdown time.
- shutdown_started_ = Time::Now();
-
- // Call FastShutdown on all of the RenderProcessHosts. This will be
- // a no-op in some cases, so we still need to go through the normal
- // shutdown path for the ones that didn't exit here.
- shutdown_num_processes_ = 0;
- shutdown_num_processes_slow_ = 0;
- RenderProcessHost::iterator hosts(RenderProcessHost::AllHostsIterator());
- while (!hosts.IsAtEnd()) {
- shutdown_num_processes_++;
- if (!hosts.GetCurrentValue()->FastShutdownIfPossible()) {
- // TODO(ojan): I think now that we deal with beforeunload/unload
- // higher up, it's not possible to get here. Confirm this and change
- // FastShutdownIfPossible to just be FastShutdown.
- shutdown_num_processes_slow_++;
- }
-
- hosts.Advance();
+ if (shutdown_type_ != NOT_VALID)
+ return;
+
+ shutdown_type_ = type;
+ // For now, we're only counting the number of renderer processes
+ // since we can't safely count the number of plugin processes from this
+ // thread, and we'd really like to avoid anything which might add further
+ // delays to shutdown time.
+ shutdown_started_ = Time::Now();
+
+ // Call FastShutdown on all of the RenderProcessHosts. This will be
+ // a no-op in some cases, so we still need to go through the normal
+ // shutdown path for the ones that didn't exit here.
+ shutdown_num_processes_ = 0;
+ shutdown_num_processes_slow_ = 0;
+ RenderProcessHost::iterator hosts(RenderProcessHost::AllHostsIterator());
+ while (!hosts.IsAtEnd()) {
+ shutdown_num_processes_++;
+ if (!hosts.GetCurrentValue()->FastShutdownIfPossible()) {
+ shutdown_num_processes_slow_++;
}
+
+ hosts.Advance();
}
}
diff --git a/chrome/browser/browser_shutdown.h b/chrome/browser/browser_shutdown.h
index 51c2b5f..49d800f 100644
--- a/chrome/browser/browser_shutdown.h
+++ b/chrome/browser/browser_shutdown.h
@@ -30,6 +30,9 @@ void RegisterPrefs(PrefService* local_state);
// time.
void OnShutdownStarting(ShutdownType type);
+// Get the current shutdown type.
+ShutdownType GetShutdownType();
+
// Invoked in two ways:
// . When the last browser has been deleted and the message loop has finished
// running.
diff --git a/chrome/browser/external_tab_container.h b/chrome/browser/external_tab_container.h
index e32d0ad..29cd62d 100644
--- a/chrome/browser/external_tab_container.h
+++ b/chrome/browser/external_tab_container.h
@@ -30,7 +30,6 @@ struct NavigationInfo;
// An external tab is a Chrome tab that is meant to displayed in an
// external process. This class provides the FocusManger needed by the
// TabContents as well as an implementation of TabContentsDelagate.
-// TODO(beng): Should override WidgetWin instead of Widget.
class ExternalTabContainer : public TabContentsDelegate,
public NotificationObserver,
public views::WidgetWin,
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 068cda8..4209774 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -195,8 +195,7 @@ BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile)
ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_(
base::TimeDelta::FromSeconds(5),
this, &BrowserRenderProcessHost::ClearTransportDIBCache)),
- zygote_child_(false),
- fast_shutdown_(false) {
+ zygote_child_(false) {
widget_helper_ = new RenderWidgetHelper();
registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
@@ -320,7 +319,7 @@ bool BrowserRenderProcessHost::Init() {
return false;
}
process_.set_handle(process);
- fast_shutdown_ = false;
+ fast_shutdown_started_ = false;
// Log the launch time, separating out the first one (which will likely be
// slower due to the rest of the browser initializing at the same time).
@@ -653,7 +652,7 @@ bool BrowserRenderProcessHost::FastShutdownIfPossible() {
if (!process_.handle())
return false; // Render process is probably crashed.
if (BrowserRenderProcessHost::run_renderer_in_process())
- return false; // Since process mode can't do fast shutdown.
+ return false; // Single process mode can't do fast shutdown.
// Test if there's an unload listener.
// NOTE: It's possible that an onunload listener may be installed
@@ -665,7 +664,7 @@ bool BrowserRenderProcessHost::FastShutdownIfPossible() {
// Check for any external tab containers, since they may still be running even
// though this window closed.
- BrowserRenderProcessHost::listeners_iterator iter(ListenersIterator());
+ listeners_iterator iter(ListenersIterator());
while (!iter.IsAtEnd()) {
// NOTE: This is a bit dangerous. We know that for now, listeners are
// always RenderWidgetHosts. But in theory, they don't have to be.
@@ -685,7 +684,7 @@ bool BrowserRenderProcessHost::FastShutdownIfPossible() {
// means that UMA won't treat this as a renderer crash.
process_.Terminate(ResultCodes::NORMAL_EXIT);
process_.Close();
- fast_shutdown_ = true;
+ fast_shutdown_started_ = true;
return true;
}
@@ -812,7 +811,7 @@ void BrowserRenderProcessHost::OnMessageReceived(const IPC::Message& msg) {
void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) {
// process_ is not NULL if we created the renderer process
if (!process_.handle()) {
- if (fast_shutdown_) {
+ if (fast_shutdown_started_) {
// We terminated the process, but the ChannelConnected task was still
// in the queue. We can safely ignore it.
return;
diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h
index 342282d..10dcbb7 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.h
+++ b/chrome/browser/renderer_host/browser_render_process_host.h
@@ -186,9 +186,6 @@ class BrowserRenderProcessHost : public RenderProcessHost,
// True iff the renderer is a child of a zygote process.
bool zygote_child_;
- // True if FastShutdownIfPossible was called and was successful.
- bool fast_shutdown_;
-
DISALLOW_COPY_AND_ASSIGN(BrowserRenderProcessHost);
};
diff --git a/chrome/browser/renderer_host/mock_render_process_host.cc b/chrome/browser/renderer_host/mock_render_process_host.cc
index 951a648b..37c60bf 100644
--- a/chrome/browser/renderer_host/mock_render_process_host.cc
+++ b/chrome/browser/renderer_host/mock_render_process_host.cc
@@ -60,7 +60,10 @@ void MockRenderProcessHost::ResetVisitedLinks() {
}
bool MockRenderProcessHost::FastShutdownIfPossible() {
- return false;
+ // We aren't actually going to do anything, but set |fast_shutdown_started_|
+ // to true so that tests know we've been called.
+ fast_shutdown_started_ = true;
+ return true;
}
bool MockRenderProcessHost::SendWithTimeout(IPC::Message* msg, int timeout_ms) {
diff --git a/chrome/browser/renderer_host/render_process_host.cc b/chrome/browser/renderer_host/render_process_host.cc
index 1dd4ec4..0844192 100644
--- a/chrome/browser/renderer_host/render_process_host.cc
+++ b/chrome/browser/renderer_host/render_process_host.cc
@@ -78,6 +78,7 @@ bool RenderProcessHost::run_renderer_in_process_ = false;
RenderProcessHost::RenderProcessHost(Profile* profile)
: max_page_id_(-1),
+ fast_shutdown_started_(false),
id_(ChildProcessInfo::GenerateChildProcessUniqueId()),
profile_(profile),
sudden_termination_allowed_(true),
@@ -120,6 +121,12 @@ void RenderProcessHost::UpdateMaxPageID(int32 page_id) {
max_page_id_ = page_id;
}
+bool RenderProcessHost::FastShutdownForPageCount(size_t count) {
+ if (listeners_.size() == count)
+ return FastShutdownIfPossible();
+ return false;
+}
+
// static
RenderProcessHost::iterator RenderProcessHost::AllHostsIterator() {
return iterator(&all_hosts);
diff --git a/chrome/browser/renderer_host/render_process_host.h b/chrome/browser/renderer_host/render_process_host.h
index 22d5b90..f266e53 100644
--- a/chrome/browser/renderer_host/render_process_host.h
+++ b/chrome/browser/renderer_host/render_process_host.h
@@ -108,6 +108,15 @@ class RenderProcessHost : public IPC::Channel::Sender,
return ignore_input_events_;
}
+ // Try to shutdown the associated render process as fast as possible, but
+ // only if |count| matches the number of render widgets that this process
+ // controls.
+ bool FastShutdownForPageCount(size_t count);
+
+ bool fast_shutdown_started() {
+ return fast_shutdown_started_;
+ }
+
// Virtual interface ---------------------------------------------------------
// Initialize the new renderer process, returning true on success. This must
@@ -220,13 +229,16 @@ class RenderProcessHost : public IPC::Channel::Sender,
// browser_process.h)
scoped_ptr<IPC::SyncChannel> channel_;
- // the registered listeners. When this list is empty or all NULL, we should
+ // The registered listeners. When this list is empty or all NULL, we should
// delete ourselves
IDMap<IPC::Channel::Listener> listeners_;
// The maximum page ID we've ever seen from the renderer process.
int32 max_page_id_;
+ // True if fast shutdown has been performed on this RPH.
+ bool fast_shutdown_started_;
+
private:
// The globally-uniqe identifier for this RPH.
int id_;
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index 73a1fa6..c59ea35 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -315,7 +315,7 @@ TabContents::TabContents(Profile* profile,
TabContents::~TabContents() {
is_being_destroyed_ = true;
- // We don't want any notifications while we're runnign our destructor.
+ // We don't want any notifications while we're running our destructor.
registrar_.RemoveAll();
// Unregister the notifications of all observed prefs change.
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());
+ }
+}