| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/memory/tab_manager.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <map> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/logging.h" | |
| 12 #include "base/macros.h" | |
| 13 #include "base/memory/ptr_util.h" | |
| 14 #include "base/metrics/field_trial.h" | |
| 15 #include "base/strings/string16.h" | |
| 16 #include "base/test/mock_entropy_provider.h" | |
| 17 #include "base/test/simple_test_tick_clock.h" | |
| 18 #include "base/time/time.h" | |
| 19 #include "build/build_config.h" | |
| 20 #include "chrome/browser/memory/tab_manager_web_contents_data.h" | |
| 21 #include "chrome/browser/memory/tab_stats.h" | |
| 22 #include "chrome/browser/profiles/profile.h" | |
| 23 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 24 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" | |
| 25 #include "chrome/common/chrome_features.h" | |
| 26 #include "chrome/common/url_constants.h" | |
| 27 #include "chrome/test/base/chrome_render_view_host_test_harness.h" | |
| 28 #include "chrome/test/base/testing_profile.h" | |
| 29 #include "components/variations/variations_associated_data.h" | |
| 30 #include "content/public/browser/render_process_host.h" | |
| 31 #include "content/public/browser/web_contents.h" | |
| 32 #include "content/public/test/mock_render_process_host.h" | |
| 33 #include "content/public/test/web_contents_tester.h" | |
| 34 #include "testing/gtest/include/gtest/gtest.h" | |
| 35 #include "url/gurl.h" | |
| 36 | |
| 37 using content::WebContents; | |
| 38 using content::WebContentsTester; | |
| 39 | |
| 40 namespace memory { | |
| 41 namespace { | |
| 42 | |
| 43 class TabStripDummyDelegate : public TestTabStripModelDelegate { | |
| 44 public: | |
| 45 TabStripDummyDelegate() {} | |
| 46 | |
| 47 bool RunUnloadListenerBeforeClosing(WebContents* contents) override { | |
| 48 return false; | |
| 49 } | |
| 50 | |
| 51 private: | |
| 52 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate); | |
| 53 }; | |
| 54 | |
| 55 class MockTabStripModelObserver : public TabStripModelObserver { | |
| 56 public: | |
| 57 MockTabStripModelObserver() | |
| 58 : nb_events_(0), old_contents_(nullptr), new_contents_(nullptr) {} | |
| 59 | |
| 60 int NbEvents() const { return nb_events_; } | |
| 61 WebContents* OldContents() const { return old_contents_; } | |
| 62 WebContents* NewContents() const { return new_contents_; } | |
| 63 | |
| 64 void Reset() { | |
| 65 nb_events_ = 0; | |
| 66 old_contents_ = nullptr; | |
| 67 new_contents_ = nullptr; | |
| 68 } | |
| 69 | |
| 70 // TabStripModelObserver implementation: | |
| 71 void TabReplacedAt(TabStripModel* tab_strip_model, | |
| 72 WebContents* old_contents, | |
| 73 WebContents* new_contents, | |
| 74 int index) override { | |
| 75 nb_events_++; | |
| 76 old_contents_ = old_contents; | |
| 77 new_contents_ = new_contents; | |
| 78 } | |
| 79 | |
| 80 private: | |
| 81 int nb_events_; | |
| 82 WebContents* old_contents_; | |
| 83 WebContents* new_contents_; | |
| 84 | |
| 85 DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver); | |
| 86 }; | |
| 87 | |
| 88 enum TestIndicies { | |
| 89 kSelected, | |
| 90 kAutoDiscardable, | |
| 91 kPinned, | |
| 92 kApp, | |
| 93 kPlayingAudio, | |
| 94 kFormEntry, | |
| 95 kRecent, | |
| 96 kOld, | |
| 97 kReallyOld, | |
| 98 kOldButPinned, | |
| 99 kInternalPage, | |
| 100 }; | |
| 101 | |
| 102 } // namespace | |
| 103 | |
| 104 class TabManagerTest : public ChromeRenderViewHostTestHarness { | |
| 105 public: | |
| 106 WebContents* CreateWebContents() { | |
| 107 return WebContents::Create(WebContents::CreateParams(profile())); | |
| 108 } | |
| 109 }; | |
| 110 | |
| 111 // TODO(georgesak): Add tests for protection to tabs with form input and | |
| 112 // playing audio; | |
| 113 | |
| 114 // Tests the sorting comparator to make sure it's producing the desired order. | |
| 115 TEST_F(TabManagerTest, Comparator) { | |
| 116 TabStatsList test_list; | |
| 117 const base::TimeTicks now = base::TimeTicks::Now(); | |
| 118 | |
| 119 // Add kSelected last to verify that the array is being sorted. | |
| 120 | |
| 121 { | |
| 122 TabStats stats; | |
| 123 stats.last_active = now; | |
| 124 stats.is_pinned = true; | |
| 125 stats.child_process_host_id = kPinned; | |
| 126 test_list.push_back(stats); | |
| 127 } | |
| 128 | |
| 129 { | |
| 130 TabStats stats; | |
| 131 stats.last_active = now; | |
| 132 stats.is_app = true; | |
| 133 stats.child_process_host_id = kApp; | |
| 134 test_list.push_back(stats); | |
| 135 } | |
| 136 | |
| 137 { | |
| 138 TabStats stats; | |
| 139 stats.last_active = now; | |
| 140 stats.is_media = true; | |
| 141 stats.child_process_host_id = kPlayingAudio; | |
| 142 test_list.push_back(stats); | |
| 143 } | |
| 144 | |
| 145 { | |
| 146 TabStats stats; | |
| 147 stats.last_active = now; | |
| 148 stats.has_form_entry = true; | |
| 149 stats.child_process_host_id = kFormEntry; | |
| 150 test_list.push_back(stats); | |
| 151 } | |
| 152 | |
| 153 { | |
| 154 TabStats stats; | |
| 155 stats.last_active = now - base::TimeDelta::FromSeconds(10); | |
| 156 stats.child_process_host_id = kRecent; | |
| 157 test_list.push_back(stats); | |
| 158 } | |
| 159 | |
| 160 { | |
| 161 TabStats stats; | |
| 162 stats.last_active = now - base::TimeDelta::FromMinutes(15); | |
| 163 stats.child_process_host_id = kOld; | |
| 164 test_list.push_back(stats); | |
| 165 } | |
| 166 | |
| 167 { | |
| 168 TabStats stats; | |
| 169 stats.last_active = now - base::TimeDelta::FromDays(365); | |
| 170 stats.child_process_host_id = kReallyOld; | |
| 171 test_list.push_back(stats); | |
| 172 } | |
| 173 | |
| 174 { | |
| 175 TabStats stats; | |
| 176 stats.is_pinned = true; | |
| 177 stats.last_active = now - base::TimeDelta::FromDays(365); | |
| 178 stats.child_process_host_id = kOldButPinned; | |
| 179 test_list.push_back(stats); | |
| 180 } | |
| 181 | |
| 182 { | |
| 183 TabStats stats; | |
| 184 stats.last_active = now; | |
| 185 stats.is_internal_page = true; | |
| 186 stats.child_process_host_id = kInternalPage; | |
| 187 test_list.push_back(stats); | |
| 188 } | |
| 189 | |
| 190 { | |
| 191 TabStats stats; | |
| 192 stats.last_active = now; | |
| 193 stats.is_auto_discardable = false; | |
| 194 stats.child_process_host_id = kAutoDiscardable; | |
| 195 test_list.push_back(stats); | |
| 196 } | |
| 197 | |
| 198 // This entry sorts to the front, so by adding it last, it verifies that the | |
| 199 // array is being sorted. | |
| 200 { | |
| 201 TabStats stats; | |
| 202 stats.last_active = now; | |
| 203 stats.is_selected = true; | |
| 204 stats.child_process_host_id = kSelected; | |
| 205 test_list.push_back(stats); | |
| 206 } | |
| 207 | |
| 208 std::sort(test_list.begin(), test_list.end(), TabManager::CompareTabStats); | |
| 209 | |
| 210 int index = 0; | |
| 211 EXPECT_EQ(kSelected, test_list[index++].child_process_host_id); | |
| 212 EXPECT_EQ(kAutoDiscardable, test_list[index++].child_process_host_id); | |
| 213 EXPECT_EQ(kFormEntry, test_list[index++].child_process_host_id); | |
| 214 EXPECT_EQ(kPlayingAudio, test_list[index++].child_process_host_id); | |
| 215 EXPECT_EQ(kPinned, test_list[index++].child_process_host_id); | |
| 216 EXPECT_EQ(kOldButPinned, test_list[index++].child_process_host_id); | |
| 217 EXPECT_EQ(kApp, test_list[index++].child_process_host_id); | |
| 218 EXPECT_EQ(kRecent, test_list[index++].child_process_host_id); | |
| 219 EXPECT_EQ(kOld, test_list[index++].child_process_host_id); | |
| 220 EXPECT_EQ(kReallyOld, test_list[index++].child_process_host_id); | |
| 221 EXPECT_EQ(kInternalPage, test_list[index++].child_process_host_id); | |
| 222 } | |
| 223 | |
| 224 TEST_F(TabManagerTest, IsInternalPage) { | |
| 225 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUIDownloadsURL))); | |
| 226 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUIHistoryURL))); | |
| 227 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUINewTabURL))); | |
| 228 EXPECT_TRUE(TabManager::IsInternalPage(GURL(chrome::kChromeUISettingsURL))); | |
| 229 | |
| 230 // Debugging URLs are not included. | |
| 231 #if defined(OS_CHROMEOS) | |
| 232 EXPECT_FALSE(TabManager::IsInternalPage(GURL(chrome::kChromeUIDiscardsURL))); | |
| 233 #endif | |
| 234 EXPECT_FALSE( | |
| 235 TabManager::IsInternalPage(GURL(chrome::kChromeUINetInternalsURL))); | |
| 236 | |
| 237 // Prefix matches are included. | |
| 238 EXPECT_TRUE( | |
| 239 TabManager::IsInternalPage(GURL("chrome://settings/fakeSetting"))); | |
| 240 } | |
| 241 | |
| 242 // Ensures discarding tabs leaves TabStripModel in a good state. | |
| 243 TEST_F(TabManagerTest, DiscardWebContentsAt) { | |
| 244 TabManager tab_manager; | |
| 245 | |
| 246 TabStripDummyDelegate delegate; | |
| 247 TabStripModel tabstrip(&delegate, profile()); | |
| 248 tabstrip.AddObserver(&tab_manager); | |
| 249 | |
| 250 // Fill it with some tabs. | |
| 251 WebContents* contents1 = CreateWebContents(); | |
| 252 tabstrip.AppendWebContents(contents1, true); | |
| 253 WebContents* contents2 = CreateWebContents(); | |
| 254 tabstrip.AppendWebContents(contents2, true); | |
| 255 | |
| 256 // Start watching for events after the appends to avoid observing state | |
| 257 // transitions that aren't relevant to this test. | |
| 258 MockTabStripModelObserver tabstrip_observer; | |
| 259 tabstrip.AddObserver(&tabstrip_observer); | |
| 260 | |
| 261 // Discard one of the tabs. | |
| 262 WebContents* null_contents1 = tab_manager.DiscardWebContentsAt(0, &tabstrip); | |
| 263 ASSERT_EQ(2, tabstrip.count()); | |
| 264 EXPECT_TRUE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0))); | |
| 265 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1))); | |
| 266 ASSERT_EQ(null_contents1, tabstrip.GetWebContentsAt(0)); | |
| 267 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1)); | |
| 268 ASSERT_EQ(1, tabstrip_observer.NbEvents()); | |
| 269 EXPECT_EQ(contents1, tabstrip_observer.OldContents()); | |
| 270 EXPECT_EQ(null_contents1, tabstrip_observer.NewContents()); | |
| 271 tabstrip_observer.Reset(); | |
| 272 | |
| 273 // Discard the same tab again, after resetting its discard state. | |
| 274 tab_manager.GetWebContentsData(tabstrip.GetWebContentsAt(0)) | |
| 275 ->SetDiscardState(false); | |
| 276 WebContents* null_contents2 = tab_manager.DiscardWebContentsAt(0, &tabstrip); | |
| 277 ASSERT_EQ(2, tabstrip.count()); | |
| 278 EXPECT_TRUE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0))); | |
| 279 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1))); | |
| 280 ASSERT_EQ(null_contents2, tabstrip.GetWebContentsAt(0)); | |
| 281 ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1)); | |
| 282 ASSERT_EQ(1, tabstrip_observer.NbEvents()); | |
| 283 EXPECT_EQ(null_contents1, tabstrip_observer.OldContents()); | |
| 284 EXPECT_EQ(null_contents2, tabstrip_observer.NewContents()); | |
| 285 | |
| 286 // Activating the tab should clear its discard state. | |
| 287 tabstrip.ActivateTabAt(0, true /* user_gesture */); | |
| 288 ASSERT_EQ(2, tabstrip.count()); | |
| 289 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0))); | |
| 290 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1))); | |
| 291 | |
| 292 // Don't discard active tab. | |
| 293 tab_manager.DiscardWebContentsAt(0, &tabstrip); | |
| 294 ASSERT_EQ(2, tabstrip.count()); | |
| 295 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(0))); | |
| 296 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1))); | |
| 297 | |
| 298 tabstrip.CloseAllTabs(); | |
| 299 EXPECT_TRUE(tabstrip.empty()); | |
| 300 } | |
| 301 | |
| 302 // Makes sure that reloading a discarded tab without activating it unmarks the | |
| 303 // tab as discarded so it won't reload on activation. | |
| 304 TEST_F(TabManagerTest, ReloadDiscardedTabContextMenu) { | |
| 305 // Note that we do not add |tab_manager| as an observer to |tabstrip| here as | |
| 306 // the event we are trying to test for is not related to the tab strip, but | |
| 307 // the web content instead and therefore should be handled by WebContentsData | |
| 308 // (which observes the web content). | |
| 309 TabManager tab_manager; | |
| 310 TabStripDummyDelegate delegate; | |
| 311 TabStripModel tabstrip(&delegate, profile()); | |
| 312 | |
| 313 // Create 2 tabs because the active tab cannot be discarded. | |
| 314 tabstrip.AppendWebContents(CreateWebContents(), true); | |
| 315 content::WebContents* test_contents = | |
| 316 WebContentsTester::CreateTestWebContents(browser_context(), nullptr); | |
| 317 tabstrip.AppendWebContents(test_contents, false); // Opened in background. | |
| 318 | |
| 319 // Navigate to a web page. This is necessary to set a current entry in memory | |
| 320 // so the reload can happen. | |
| 321 WebContentsTester::For(test_contents) | |
| 322 ->NavigateAndCommit(GURL("chrome://newtab")); | |
| 323 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1))); | |
| 324 | |
| 325 tab_manager.DiscardWebContentsAt(1, &tabstrip); | |
| 326 EXPECT_TRUE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1))); | |
| 327 | |
| 328 tabstrip.GetWebContentsAt(1)->GetController().Reload( | |
| 329 content::ReloadType::NORMAL, false); | |
| 330 EXPECT_FALSE(tab_manager.IsTabDiscarded(tabstrip.GetWebContentsAt(1))); | |
| 331 tabstrip.CloseAllTabs(); | |
| 332 EXPECT_TRUE(tabstrip.empty()); | |
| 333 } | |
| 334 | |
| 335 // Makes sure that the last active time property is saved even though the tab is | |
| 336 // discarded. | |
| 337 TEST_F(TabManagerTest, DiscardedTabKeepsLastActiveTime) { | |
| 338 TabManager tab_manager; | |
| 339 TabStripDummyDelegate delegate; | |
| 340 TabStripModel tabstrip(&delegate, profile()); | |
| 341 tabstrip.AddObserver(&tab_manager); | |
| 342 | |
| 343 tabstrip.AppendWebContents(CreateWebContents(), true); | |
| 344 WebContents* test_contents = CreateWebContents(); | |
| 345 tabstrip.AppendWebContents(test_contents, false); | |
| 346 | |
| 347 // Simulate an old inactive tab about to get discarded. | |
| 348 base::TimeTicks new_last_active_time = | |
| 349 base::TimeTicks::Now() - base::TimeDelta::FromMinutes(35); | |
| 350 test_contents->SetLastActiveTime(new_last_active_time); | |
| 351 EXPECT_EQ(new_last_active_time, test_contents->GetLastActiveTime()); | |
| 352 | |
| 353 WebContents* null_contents = tab_manager.DiscardWebContentsAt(1, &tabstrip); | |
| 354 EXPECT_EQ(new_last_active_time, null_contents->GetLastActiveTime()); | |
| 355 | |
| 356 tabstrip.CloseAllTabs(); | |
| 357 EXPECT_TRUE(tabstrip.empty()); | |
| 358 } | |
| 359 | |
| 360 // Test to see if a tab can only be discarded once. On Windows and Mac, this | |
| 361 // defaults to true unless overridden through a variation parameter. On other | |
| 362 // platforms, it's always false. | |
| 363 #if defined(OS_WIN) || defined(OS_MACOSX) | |
| 364 TEST_F(TabManagerTest, CanOnlyDiscardOnce) { | |
| 365 TabManager tab_manager; | |
| 366 const std::string kTrialName = features::kAutomaticTabDiscarding.name; | |
| 367 | |
| 368 // Not setting the variation parameter. | |
| 369 { | |
| 370 bool discard_once_value = tab_manager.CanOnlyDiscardOnce(); | |
| 371 EXPECT_TRUE(discard_once_value); | |
| 372 } | |
| 373 | |
| 374 // Setting the variation parameter to true. | |
| 375 { | |
| 376 std::unique_ptr<base::FieldTrialList> field_trial_list_; | |
| 377 field_trial_list_.reset( | |
| 378 new base::FieldTrialList( | |
| 379 base::MakeUnique<base::MockEntropyProvider>())); | |
| 380 variations::testing::ClearAllVariationParams(); | |
| 381 | |
| 382 std::map<std::string, std::string> params; | |
| 383 params["AllowMultipleDiscards"] = "true"; | |
| 384 ASSERT_TRUE(variations::AssociateVariationParams(kTrialName, "A", params)); | |
| 385 base::FieldTrialList::CreateFieldTrial(kTrialName, "A"); | |
| 386 | |
| 387 bool discard_once_value = tab_manager.CanOnlyDiscardOnce(); | |
| 388 EXPECT_FALSE(discard_once_value); | |
| 389 } | |
| 390 | |
| 391 // Setting the variation parameter to something else. | |
| 392 { | |
| 393 std::unique_ptr<base::FieldTrialList> field_trial_list_; | |
| 394 field_trial_list_.reset( | |
| 395 new base::FieldTrialList( | |
| 396 base::MakeUnique<base::MockEntropyProvider>())); | |
| 397 variations::testing::ClearAllVariationParams(); | |
| 398 | |
| 399 std::map<std::string, std::string> params; | |
| 400 params["AllowMultipleDiscards"] = "somethingElse"; | |
| 401 ASSERT_TRUE(variations::AssociateVariationParams(kTrialName, "B", params)); | |
| 402 base::FieldTrialList::CreateFieldTrial(kTrialName, "B"); | |
| 403 | |
| 404 bool discard_once_value = tab_manager.CanOnlyDiscardOnce(); | |
| 405 EXPECT_TRUE(discard_once_value); | |
| 406 } | |
| 407 } | |
| 408 #else | |
| 409 TEST_F(TabManagerTest, CanOnlyDiscardOnce) { | |
| 410 TabManager tab_manager; | |
| 411 | |
| 412 bool discard_once_value = tab_manager.CanOnlyDiscardOnce(); | |
| 413 EXPECT_FALSE(discard_once_value); | |
| 414 } | |
| 415 #endif // defined(OS_WIN) || defined(OS_MACOSX) | |
| 416 | |
| 417 TEST_F(TabManagerTest, DefaultTimeToPurgeInCorrectRange) { | |
| 418 TabManager tab_manager; | |
| 419 base::TimeDelta time_to_purge = | |
| 420 tab_manager.GetTimeToPurge(TabManager::kDefaultMinTimeToPurge); | |
| 421 EXPECT_GE(time_to_purge, base::TimeDelta::FromMinutes(30)); | |
| 422 EXPECT_LT(time_to_purge, base::TimeDelta::FromMinutes(60)); | |
| 423 } | |
| 424 | |
| 425 TEST_F(TabManagerTest, ShouldPurgeAtDefaultTime) { | |
| 426 TabManager tab_manager; | |
| 427 TabStripDummyDelegate delegate; | |
| 428 TabStripModel tabstrip(&delegate, profile()); | |
| 429 tabstrip.AddObserver(&tab_manager); | |
| 430 | |
| 431 WebContents* test_contents = CreateWebContents(); | |
| 432 tabstrip.AppendWebContents(test_contents, false); | |
| 433 | |
| 434 base::SimpleTestTickClock test_clock; | |
| 435 tab_manager.set_test_tick_clock(&test_clock); | |
| 436 | |
| 437 tab_manager.GetWebContentsData(test_contents)->set_is_purged(false); | |
| 438 tab_manager.GetWebContentsData(test_contents) | |
| 439 ->SetLastInactiveTime(test_clock.NowTicks()); | |
| 440 tab_manager.GetWebContentsData(test_contents) | |
| 441 ->set_time_to_purge(base::TimeDelta::FromMinutes(1)); | |
| 442 | |
| 443 // Wait 1 minute and verify that the tab is still not to be purged. | |
| 444 test_clock.Advance(base::TimeDelta::FromMinutes(1)); | |
| 445 EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents)); | |
| 446 | |
| 447 // Wait another 1 second and verify that it should be purged now . | |
| 448 test_clock.Advance(base::TimeDelta::FromSeconds(1)); | |
| 449 EXPECT_TRUE(tab_manager.ShouldPurgeNow(test_contents)); | |
| 450 | |
| 451 tab_manager.GetWebContentsData(test_contents)->set_is_purged(true); | |
| 452 tab_manager.GetWebContentsData(test_contents) | |
| 453 ->SetLastInactiveTime(test_clock.NowTicks()); | |
| 454 | |
| 455 // Wait 1 day and verify that the tab is still be purged. | |
| 456 test_clock.Advance(base::TimeDelta::FromHours(24)); | |
| 457 EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents)); | |
| 458 } | |
| 459 | |
| 460 TEST_F(TabManagerTest, ActivateTabResetPurgeState) { | |
| 461 TabManager tab_manager; | |
| 462 TabStripDummyDelegate delegate; | |
| 463 TabStripModel tabstrip(&delegate, profile()); | |
| 464 tabstrip.AddObserver(&tab_manager); | |
| 465 tab_manager.test_tab_strip_models_.push_back( | |
| 466 TabManager::TestTabStripModel(&tabstrip, false /* !is_app */)); | |
| 467 | |
| 468 base::SimpleTestTickClock test_clock; | |
| 469 tab_manager.set_test_tick_clock(&test_clock); | |
| 470 | |
| 471 WebContents* tab1 = CreateWebContents(); | |
| 472 WebContents* tab2 = CreateWebContents(); | |
| 473 tabstrip.AppendWebContents(tab1, true); | |
| 474 tabstrip.AppendWebContents(tab2, false); | |
| 475 | |
| 476 tab_manager.GetWebContentsData(tab2)->SetLastInactiveTime( | |
| 477 test_clock.NowTicks()); | |
| 478 static_cast<content::MockRenderProcessHost*>(tab2->GetRenderProcessHost()) | |
| 479 ->set_is_process_backgrounded(true); | |
| 480 EXPECT_TRUE(tab2->GetRenderProcessHost()->IsProcessBackgrounded()); | |
| 481 | |
| 482 // Initially PurgeAndSuspend state should be NOT_PURGED. | |
| 483 EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged()); | |
| 484 tab_manager.GetWebContentsData(tab2)->set_time_to_purge( | |
| 485 base::TimeDelta::FromMinutes(1)); | |
| 486 test_clock.Advance(base::TimeDelta::FromMinutes(2)); | |
| 487 tab_manager.PurgeBackgroundedTabsIfNeeded(); | |
| 488 // Since tab2 is kept inactive and background for more than time-to-purge, | |
| 489 // tab2 should be purged. | |
| 490 EXPECT_TRUE(tab_manager.GetWebContentsData(tab2)->is_purged()); | |
| 491 | |
| 492 // Activate tab2. Tab2's PurgeAndSuspend state should be NOT_PURGED. | |
| 493 tabstrip.ActivateTabAt(1, true /* user_gesture */); | |
| 494 EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged()); | |
| 495 } | |
| 496 | |
| 497 } // namespace memory | |
| OLD | NEW |