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 | |
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')
-rw-r--r-- | chrome/browser/browser.cc | 1 | ||||
-rw-r--r-- | chrome/browser/browser_shutdown.cc | 52 | ||||
-rw-r--r-- | chrome/browser/browser_shutdown.h | 3 | ||||
-rw-r--r-- | chrome/browser/external_tab_container.h | 1 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.cc | 13 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.h | 3 | ||||
-rw-r--r-- | chrome/browser/renderer_host/mock_render_process_host.cc | 5 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_process_host.cc | 7 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_process_host.h | 14 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents.cc | 2 | ||||
-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 |
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()); + } +} |