| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/observer_list.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" |
| #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h" |
| #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h" |
| #include "chrome/browser/notifications/notification_permission_context.h" |
| #include "chrome/browser/resource_coordinator/intervention_policy_database.h" |
| #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h" |
| #include "chrome/browser/resource_coordinator/tab_helper.h" |
| #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h" |
| #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h" |
| #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h" |
| #include "chrome/browser/resource_coordinator/tab_load_tracker.h" |
| #include "chrome/browser/resource_coordinator/tab_manager_features.h" |
| #include "chrome/browser/resource_coordinator/test_lifecycle_unit.h" |
| #include "chrome/browser/resource_coordinator/time.h" |
| #include "chrome/browser/resource_coordinator/usage_clock.h" |
| #include "chrome/browser/resource_coordinator/utils.h" |
| #include "chrome/browser/tab_contents/form_interaction_tab_helper.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" |
| #include "chrome/browser/usb/frame_usb_services.h" |
| #include "chrome/browser/usb/usb_chooser_context.h" |
| #include "chrome/browser/usb/usb_chooser_context_factory.h" |
| #include "chrome/browser/usb/usb_tab_helper.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/referrer.h" |
| #include "content/public/test/navigation_simulator.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "services/device/public/cpp/test/fake_usb_device_manager.h" |
| #include "services/device/public/mojom/usb_manager.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h" |
| |
| namespace resource_coordinator { |
| |
| namespace { |
| |
| using LoadingState = TabLoadTracker::LoadingState; |
| |
| constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromSeconds(1); |
| |
| class MockTabLifecycleObserver : public TabLifecycleObserver { |
| public: |
| MockTabLifecycleObserver() = default; |
| |
| MOCK_METHOD3(OnDiscardedStateChange, |
| void(content::WebContents* contents, |
| LifecycleUnitDiscardReason reason, |
| bool is_discarded)); |
| MOCK_METHOD2(OnAutoDiscardableStateChange, |
| void(content::WebContents* contents, bool is_auto_discardable)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockTabLifecycleObserver); |
| }; |
| |
| } // namespace |
| |
| class MockLifecycleUnitObserver : public LifecycleUnitObserver { |
| public: |
| MockLifecycleUnitObserver() = default; |
| |
| MOCK_METHOD3(OnLifecycleUnitStateChanged, |
| void(LifecycleUnit*, |
| LifecycleUnitState, |
| LifecycleUnitStateChangeReason)); |
| MOCK_METHOD1(OnLifecycleUnitDestroyed, void(LifecycleUnit*)); |
| MOCK_METHOD2(OnLifecycleUnitVisibilityChanged, |
| void(LifecycleUnit*, content::Visibility)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockLifecycleUnitObserver); |
| }; |
| |
| class TabLifecycleUnitTest : public ChromeRenderViewHostTestHarness { |
| protected: |
| using TabLifecycleUnit = TabLifecycleUnitSource::TabLifecycleUnit; |
| |
| // This is an internal class so that it is also friends with |
| // TabLifecycleUnitTest. |
| class ScopedEnterpriseOptOut; |
| |
| TabLifecycleUnitTest() : scoped_set_tick_clock_for_testing_(&test_clock_) { |
| // Advance the clock so that it doesn't yield null time ticks. |
| test_clock_.Advance(base::TimeDelta::FromSeconds(1)); |
| observers_.AddObserver(&observer_); |
| } |
| |
| void SetUp() override { |
| ChromeRenderViewHostTestHarness::SetUp(); |
| |
| metrics::DesktopSessionDurationTracker::Initialize(); |
| usage_clock_ = std::make_unique<UsageClock>(); |
| |
| // Force TabManager/TabLifecycleUnitSource creation. |
| g_browser_process->GetTabManager(); |
| |
| std::unique_ptr<content::WebContents> test_web_contents = |
| CreateTestWebContents(); |
| web_contents_ = test_web_contents.get(); |
| auto* tester = content::WebContentsTester::For(web_contents_); |
| tester->SetLastActiveTime(NowTicks()); |
| ResourceCoordinatorTabHelper::CreateForWebContents(web_contents_); |
| // Commit an URL to allow discarding. |
| auto navigation = content::NavigationSimulator::CreateBrowserInitiated( |
| GURL("https://www.example.com"), web_contents_); |
| navigation->SetKeepLoading(true); |
| navigation->Commit(); |
| |
| tab_strip_model_ = |
| std::make_unique<TabStripModel>(&tab_strip_model_delegate_, profile()); |
| tab_strip_model_->AppendWebContents(std::move(test_web_contents), false); |
| web_contents_->WasHidden(); |
| |
| std::unique_ptr<content::WebContents> second_web_contents = |
| CreateTestWebContents(); |
| content::WebContents* raw_second_web_contents = second_web_contents.get(); |
| tab_strip_model_->AppendWebContents(std::move(second_web_contents), |
| /*foreground=*/true); |
| raw_second_web_contents->WasHidden(); |
| } |
| |
| void TearDown() override { |
| while (!tab_strip_model_->empty()) |
| tab_strip_model_->DetachWebContentsAt(0); |
| tab_strip_model_.reset(); |
| usage_clock_.reset(); |
| metrics::DesktopSessionDurationTracker::CleanupForTesting(); |
| ChromeRenderViewHostTestHarness::TearDown(); |
| } |
| |
| // Create a new test WebContents and append it to the tab strip to allow |
| // testing discarding operations on it. The returned WebContents is in the |
| // hidden state. |
| content::WebContents* AddNewHiddenWebContentsToTabStrip() { |
| std::unique_ptr<content::WebContents> test_web_contents = |
| CreateTestWebContents(); |
| content::WebContents* web_contents = test_web_contents.get(); |
| ResourceCoordinatorTabHelper::CreateForWebContents(web_contents); |
| tab_strip_model_->AppendWebContents(std::move(test_web_contents), false); |
| web_contents->WasHidden(); |
| return web_contents; |
| } |
| |
| ::testing::StrictMock<MockTabLifecycleObserver> observer_; |
| base::ObserverList<TabLifecycleObserver>::Unchecked observers_; |
| content::WebContents* web_contents_; // Owned by tab_strip_model_. |
| std::unique_ptr<TabStripModel> tab_strip_model_; |
| base::SimpleTestTickClock test_clock_; |
| std::unique_ptr<UsageClock> usage_clock_; |
| |
| private: |
| // So that the main thread looks like the UI thread as expected. |
| TestTabStripModelDelegate tab_strip_model_delegate_; |
| ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitTest); |
| }; |
| |
| class TabLifecycleUnitTest::ScopedEnterpriseOptOut { |
| public: |
| ScopedEnterpriseOptOut() { |
| GetTabLifecycleUnitSource()->SetTabLifecyclesEnterprisePolicy(false); |
| } |
| |
| ~ScopedEnterpriseOptOut() { |
| GetTabLifecycleUnitSource()->SetTabLifecyclesEnterprisePolicy(true); |
| } |
| }; |
| |
| TEST_F(TabLifecycleUnitTest, AsTabLifecycleUnitExternal) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| EXPECT_TRUE(tab_lifecycle_unit.AsTabLifecycleUnitExternal()); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CanDiscardByDefault) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, SetFocused) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| EXPECT_EQ(NowTicks(), tab_lifecycle_unit.GetLastFocusedTime()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| tab_lifecycle_unit.SetFocused(true); |
| tab_strip_model_->ActivateTabAt(0); |
| web_contents_->WasShown(); |
| EXPECT_EQ(base::TimeTicks::Max(), tab_lifecycle_unit.GetLastFocusedTime()); |
| ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit, |
| DecisionFailureReason::LIVE_STATE_VISIBLE); |
| |
| tab_lifecycle_unit.SetFocused(false); |
| tab_strip_model_->ActivateTabAt(1); |
| web_contents_->WasHidden(); |
| EXPECT_EQ(test_clock_.NowTicks(), tab_lifecycle_unit.GetLastFocusedTime()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, AutoDiscardable) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| EXPECT_TRUE(tab_lifecycle_unit.IsAutoDiscardable()); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| EXPECT_CALL(observer_, OnAutoDiscardableStateChange(web_contents_, false)); |
| tab_lifecycle_unit.SetAutoDiscardable(false); |
| ::testing::Mock::VerifyAndClear(&observer_); |
| EXPECT_FALSE(tab_lifecycle_unit.IsAutoDiscardable()); |
| ExpectCanDiscardFalseAllReasons( |
| &tab_lifecycle_unit, |
| DecisionFailureReason::LIVE_STATE_EXTENSION_DISALLOWED); |
| |
| EXPECT_CALL(observer_, OnAutoDiscardableStateChange(web_contents_, true)); |
| tab_lifecycle_unit.SetAutoDiscardable(true); |
| ::testing::Mock::VerifyAndClear(&observer_); |
| EXPECT_TRUE(tab_lifecycle_unit.IsAutoDiscardable()); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardCrashed) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| |
| web_contents_->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, 0); |
| ExpectCanDiscardFalseTrivialAllReasons(&tab_lifecycle_unit); |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS_ASH) |
| TEST_F(TabLifecycleUnitTest, CannotDiscardActive) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| |
| tab_strip_model_->ActivateTabAt(0); |
| |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| |
| ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit, |
| DecisionFailureReason::LIVE_STATE_VISIBLE); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, UrgentDiscardProtections) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Initial external discarding are fine, but urgent discarding is blocked |
| // because the tab is too recent. |
| ExpectCanDiscardTrue(&tab_lifecycle_unit, |
| LifecycleUnitDiscardReason::EXTERNAL); |
| ExpectCanDiscardFalseTrivial(&tab_lifecycle_unit, |
| LifecycleUnitDiscardReason::URGENT); |
| |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| |
| // The tab should now be discardable for all reasons. |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| // Mark the tab as having been discarded. |
| tab_lifecycle_unit.SetDiscardCountForTesting(1); |
| |
| // Advance time enough that the time protection no longer applies. The tab |
| // should still not be urgent discardable at this point, because it has |
| // already been discarded at least once. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| |
| // External discarding should be fine, but not urgent. |
| ExpectCanDiscardTrue(&tab_lifecycle_unit, |
| LifecycleUnitDiscardReason::EXTERNAL); |
| ExpectCanDiscardFalseTrivial(&tab_lifecycle_unit, |
| LifecycleUnitDiscardReason::URGENT); |
| |
| // The tab should be discardable a second time when the memory limit |
| // enterprise policy is set. |
| GetTabLifecycleUnitSource()->SetMemoryLimitEnterprisePolicyFlag(true); |
| ExpectCanDiscardTrue(&tab_lifecycle_unit, LifecycleUnitDiscardReason::URGENT); |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardInvalidURL) { |
| content::WebContents* web_contents = AddNewHiddenWebContentsToTabStrip(); |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents, |
| tab_strip_model_.get()); |
| // TODO(sebmarchand): Fix this test, this doesn't really test that it's not |
| // possible to discard an invalid URL, TestWebContents::GetLastCommittedURL() |
| // doesn't return the URL set with "SetLastCommittedURL" if this one is |
| // invalid. |
| content::WebContentsTester::For(web_contents) |
| ->SetLastCommittedURL(GURL("Invalid :)")); |
| ExpectCanDiscardFalseTrivialAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardEmptyURL) { |
| content::WebContents* web_contents = AddNewHiddenWebContentsToTabStrip(); |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents, |
| tab_strip_model_.get()); |
| |
| ExpectCanDiscardFalseTrivialAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardVideoCapture) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| blink::MediaStreamDevices video_devices{blink::MediaStreamDevice( |
| blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "fake_media_device", |
| "fake_media_device")}; |
| std::unique_ptr<content::MediaStreamUI> ui = |
| MediaCaptureDevicesDispatcher::GetInstance() |
| ->GetMediaStreamCaptureIndicator() |
| ->RegisterMediaStream(web_contents_, video_devices); |
| ui->OnStarted(base::OnceClosure(), content::MediaStreamUI::SourceCallback(), |
| /*label=*/std::string(), /*screen_capture_ids=*/{}, |
| content::MediaStreamUI::StateChangeCallback()); |
| ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit, |
| DecisionFailureReason::LIVE_STATE_CAPTURING); |
| |
| ui.reset(); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardHasFormInteractions) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| FormInteractionTabHelper::CreateForWebContents(web_contents_); |
| FormInteractionTabHelper::FromWebContents(web_contents_) |
| ->OnHadFormInteractionChangedForTesting(true); |
| ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit, |
| DecisionFailureReason::LIVE_STATE_FORM_ENTRY); |
| |
| FormInteractionTabHelper::FromWebContents(web_contents_) |
| ->OnHadFormInteractionChangedForTesting(false); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardDesktopCapture) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| blink::MediaStreamDevices desktop_capture_devices{blink::MediaStreamDevice( |
| blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, |
| "fake_media_device", "fake_media_device")}; |
| std::unique_ptr<content::MediaStreamUI> ui = |
| MediaCaptureDevicesDispatcher::GetInstance() |
| ->GetMediaStreamCaptureIndicator() |
| ->RegisterMediaStream(web_contents_, desktop_capture_devices); |
| ui->OnStarted(base::OnceClosure(), content::MediaStreamUI::SourceCallback(), |
| /*label=*/std::string(), /*screen_capture_ids=*/{}, |
| content::MediaStreamUI::StateChangeCallback()); |
| ExpectCanDiscardFalseAllReasons( |
| &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_DESKTOP_CAPTURE); |
| |
| ui.reset(); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardRecentlyAudible) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| // Cannot discard when the "recently audible" bit is set. |
| tab_lifecycle_unit.SetRecentlyAudible(true); |
| ExpectCanDiscardFalseAllReasons( |
| &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO); |
| |
| // The "recently audible" bit is still set. The tab cannot be discarded. |
| test_clock_.Advance(kTabAudioProtectionTime); |
| ExpectCanDiscardFalseAllReasons( |
| &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO); |
| |
| // The "recently audible" bit was unset less than |
| // kTabAudioProtectionTime ago. The tab cannot be discarded. |
| tab_lifecycle_unit.SetRecentlyAudible(false); |
| test_clock_.Advance(kShortDelay); |
| ExpectCanDiscardFalseAllReasons( |
| &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO); |
| |
| // The "recently audible" bit was unset kTabAudioProtectionTime ago. The tab |
| // can be discarded. |
| test_clock_.Advance(kTabAudioProtectionTime - kShortDelay); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| // Calling SetRecentlyAudible(false) again does not change the fact that the |
| // tab can be discarded. |
| tab_lifecycle_unit.SetRecentlyAudible(false); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CanDiscardNeverAudibleTab) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| tab_lifecycle_unit.SetRecentlyAudible(false); |
| // Since the tab was never audible, it should be possible to discard it, |
| // even if there was a recent call to SetRecentlyAudible(false). |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, CannotDiscardPDF) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| // Advance time enough that the tab is urgent discardable. |
| test_clock_.Advance(kBackgroundUrgentProtectionTime); |
| ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit); |
| |
| content::WebContentsTester::For(web_contents_) |
| ->SetMainFrameMimeType("application/pdf"); |
| ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit, |
| DecisionFailureReason::LIVE_STATE_IS_PDF); |
| } |
| |
| TEST_F(TabLifecycleUnitTest, NotifiedOfWebContentsVisibilityChanges) { |
| TabLifecycleUnit tab_lifecycle_unit(GetTabLifecycleUnitSource(), &observers_, |
| usage_clock_.get(), web_contents_, |
| tab_strip_model_.get()); |
| |
| ::testing::StrictMock<MockLifecycleUnitObserver> observer; |
| tab_lifecycle_unit.AddObserver(&observer); |
| |
| EXPECT_CALL(observer, OnLifecycleUnitVisibilityChanged( |
| &tab_lifecycle_unit, content::Visibility::VISIBLE)); |
| web_contents_->WasShown(); |
| ::testing::Mock::VerifyAndClear(&observer); |
| |
| EXPECT_CALL(observer, OnLifecycleUnitVisibilityChanged( |
| &tab_lifecycle_unit, content::Visibility::HIDDEN)); |
| web_contents_->WasHidden(); |
| ::testing::Mock::VerifyAndClear(&observer); |
| |
| EXPECT_CALL(observer, OnLifecycleUnitVisibilityChanged( |
| &tab_lifecycle_unit, content::Visibility::VISIBLE)); |
| web_contents_->WasShown(); |
| ::testing::Mock::VerifyAndClear(&observer); |
| |
| EXPECT_CALL(observer, |
| OnLifecycleUnitVisibilityChanged(&tab_lifecycle_unit, |
| content::Visibility::OCCLUDED)); |
| web_contents_->WasOccluded(); |
| ::testing::Mock::VerifyAndClear(&observer); |
| |
| tab_lifecycle_unit.RemoveObserver(&observer); |
| } |
| |
| } // namespace resource_coordinator |