| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "android_webview/browser/lifecycle/aw_contents_lifecycle_notifier.h" |
| |
| #include "base/compiler_specific.h" |
| #include "base/memory/raw_ptr.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace android_webview { |
| |
| class TestWebViewAppObserver : public WebViewAppStateObserver { |
| public: |
| TestWebViewAppObserver() = default; |
| ~TestWebViewAppObserver() override = default; |
| |
| // WebViewAppStateObserver. |
| void OnAppStateChanged(State state) override { state_ = state; } |
| |
| WebViewAppStateObserver::State state() const { return state_; } |
| |
| private: |
| WebViewAppStateObserver::State state_; |
| }; |
| |
| class TestOnLoseForegroundCallback { |
| public: |
| explicit TestOnLoseForegroundCallback(const TestWebViewAppObserver* other) |
| : other_(other) {} |
| |
| ~TestOnLoseForegroundCallback() = default; |
| |
| void OnLoseForeground() { |
| ASSERT_NE(other_->state(), WebViewAppStateObserver::State::kForeground); |
| called_ = true; |
| } |
| bool called() const { return called_; } |
| |
| private: |
| bool called_ = false; |
| raw_ptr<const TestWebViewAppObserver> other_; |
| }; |
| |
| class TestAwContentsLifecycleNotifier : public AwContentsLifecycleNotifier { |
| public: |
| explicit TestAwContentsLifecycleNotifier(OnLoseForegroundCallback callback) |
| : AwContentsLifecycleNotifier(callback) {} |
| ~TestAwContentsLifecycleNotifier() override = default; |
| |
| size_t GetAwContentsStateCount(AwContentsState state) const { |
| return UNSAFE_TODO(state_count_[ToIndex(state)]); |
| } |
| |
| bool HasAwContentsInstanceForTesting() const { |
| return this->HasAwContentsInstance(); |
| } |
| }; |
| |
| class AwContentsLifecycleNotifierTest : public testing::Test { |
| public: |
| WebViewAppStateObserver::State GetState() const { return observer_->state(); } |
| size_t GetAwContentsStateCount( |
| AwContentsLifecycleNotifier::AwContentsState state) const { |
| return notifier_->GetAwContentsStateCount(state); |
| } |
| |
| bool HasAwContentsInstance() const { |
| return notifier_->HasAwContentsInstanceForTesting(); |
| } |
| |
| bool HasAwContentsEverCreated() const { |
| return notifier_->has_aw_contents_ever_created(); |
| } |
| |
| AwContentsLifecycleNotifier* notifier() { return notifier_.get(); } |
| |
| void VerifyAwContentsStateCount(size_t detached_count, |
| size_t foreground_count, |
| size_t background_count) { |
| ASSERT_EQ(GetAwContentsStateCount( |
| AwContentsLifecycleNotifier::AwContentsState::kDetached), |
| detached_count); |
| ASSERT_EQ(GetAwContentsStateCount( |
| AwContentsLifecycleNotifier::AwContentsState::kForeground), |
| foreground_count); |
| ASSERT_EQ(GetAwContentsStateCount( |
| AwContentsLifecycleNotifier::AwContentsState::kBackground), |
| background_count); |
| } |
| |
| const TestWebViewAppObserver* observer() const { return observer_.get(); } |
| const TestOnLoseForegroundCallback* callback() const { |
| return callback_.get(); |
| } |
| |
| protected: |
| // testing::Test. |
| void SetUp() override { |
| AwContentsLifecycleNotifier::InitForTesting(); |
| observer_ = std::make_unique<TestWebViewAppObserver>(); |
| callback_ = std::make_unique<TestOnLoseForegroundCallback>(observer_.get()); |
| notifier_ = std::make_unique<TestAwContentsLifecycleNotifier>( |
| base::BindRepeating(&TestOnLoseForegroundCallback::OnLoseForeground, |
| base::Unretained(callback_.get()))); |
| |
| notifier_->AddObserver(observer_.get()); |
| } |
| |
| void TearDown() override { notifier_->RemoveObserver(observer_.get()); } |
| |
| private: |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<TestWebViewAppObserver> observer_; |
| std::unique_ptr<TestOnLoseForegroundCallback> callback_; |
| std::unique_ptr<TestAwContentsLifecycleNotifier> notifier_; |
| }; |
| |
| TEST_F(AwContentsLifecycleNotifierTest, Created) { |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_FALSE(HasAwContentsEverCreated()); |
| ASSERT_FALSE(HasAwContentsInstance()); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kUnknown); |
| ASSERT_TRUE(HasAwContentsInstance()); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewDestroyed(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 0, 0); |
| ASSERT_FALSE(HasAwContentsInstance()); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, AttachToAndDetachFromWindow) { |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_FALSE(HasAwContentsEverCreated()); |
| ASSERT_FALSE(HasAwContentsInstance()); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 0, 1u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kBackground); |
| ASSERT_TRUE(HasAwContentsInstance()); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewDetachedFromWindow(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kUnknown); |
| ASSERT_TRUE(HasAwContentsInstance()); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewDestroyed(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 0, 0); |
| ASSERT_FALSE(HasAwContentsInstance()); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, WindowVisibleAndInvisible) { |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_FALSE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents); |
| notifier()->OnWebViewWindowBeVisible(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 1u, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kForeground); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewWindowBeInvisible(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 0, 1u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kBackground); |
| |
| notifier()->OnWebViewDetachedFromWindow(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kUnknown); |
| |
| notifier()->OnWebViewDestroyed(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, MultipleAwContents) { |
| const AwContents* fake_aw_contents1 = reinterpret_cast<const AwContents*>(1); |
| const AwContents* fake_aw_contents2 = reinterpret_cast<const AwContents*>(2); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_FALSE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents1); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kUnknown); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents1); |
| VerifyAwContentsStateCount(0, 0, 1u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kBackground); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents2); |
| VerifyAwContentsStateCount(1u, 0, 1u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kBackground); |
| |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents2); |
| VerifyAwContentsStateCount(0, 0, 2u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kBackground); |
| |
| notifier()->OnWebViewWindowBeVisible(fake_aw_contents2); |
| VerifyAwContentsStateCount(0, 1u, 1u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kForeground); |
| |
| notifier()->OnWebViewWindowBeVisible(fake_aw_contents1); |
| VerifyAwContentsStateCount(0, 2u, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kForeground); |
| |
| notifier()->OnWebViewDestroyed(fake_aw_contents2); |
| VerifyAwContentsStateCount(0, 1u, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kForeground); |
| |
| notifier()->OnWebViewWindowBeInvisible(fake_aw_contents1); |
| VerifyAwContentsStateCount(0, 0, 1u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kBackground); |
| |
| notifier()->OnWebViewDetachedFromWindow(fake_aw_contents1); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kUnknown); |
| |
| notifier()->OnWebViewDestroyed(fake_aw_contents1); |
| VerifyAwContentsStateCount(0, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents1); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kUnknown); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, AttachedToWindowAfterWindowVisible) { |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_FALSE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| notifier()->OnWebViewWindowBeVisible(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 1u, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kForeground); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, AttachedToWindowAfterWindowInvisible) { |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_FALSE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| notifier()->OnWebViewWindowBeInvisible(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 0, 1u); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kBackground); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, DetachFromVisibleWindow) { |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kDestroyed); |
| ASSERT_FALSE(HasAwContentsEverCreated()); |
| |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| notifier()->OnWebViewWindowBeVisible(fake_aw_contents); |
| VerifyAwContentsStateCount(1u, 0, 0); |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents); |
| VerifyAwContentsStateCount(0, 1u, 0); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kForeground); |
| notifier()->OnWebViewDetachedFromWindow(fake_aw_contents); |
| ASSERT_EQ(GetState(), WebViewAppStateObserver::State::kUnknown); |
| ASSERT_TRUE(HasAwContentsEverCreated()); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, GetAllAwContents) { |
| std::vector<const AwContents*> all_aw_contents( |
| notifier()->GetAllAwContents()); |
| ASSERT_TRUE(all_aw_contents.empty()); |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| all_aw_contents = notifier()->GetAllAwContents(); |
| ASSERT_EQ(all_aw_contents.size(), 1u); |
| ASSERT_EQ(all_aw_contents.back(), fake_aw_contents); |
| const AwContents* fake_aw_contents2 = reinterpret_cast<const AwContents*>(2); |
| notifier()->OnWebViewCreated(fake_aw_contents2); |
| all_aw_contents = notifier()->GetAllAwContents(); |
| ASSERT_EQ(all_aw_contents.size(), 2u); |
| ASSERT_EQ(all_aw_contents.front(), fake_aw_contents); |
| ASSERT_EQ(all_aw_contents.back(), fake_aw_contents2); |
| notifier()->OnWebViewDestroyed(fake_aw_contents); |
| all_aw_contents = notifier()->GetAllAwContents(); |
| ASSERT_EQ(all_aw_contents.size(), 1u); |
| ASSERT_EQ(all_aw_contents.back(), fake_aw_contents2); |
| notifier()->OnWebViewDestroyed(fake_aw_contents2); |
| all_aw_contents = notifier()->GetAllAwContents(); |
| ASSERT_TRUE(all_aw_contents.empty()); |
| } |
| |
| TEST_F(AwContentsLifecycleNotifierTest, LoseForegroundCallback) { |
| const AwContents* fake_aw_contents = reinterpret_cast<const AwContents*>(1); |
| notifier()->OnWebViewCreated(fake_aw_contents); |
| notifier()->OnWebViewAttachedToWindow(fake_aw_contents); |
| notifier()->OnWebViewWindowBeVisible(fake_aw_contents); |
| notifier()->OnWebViewWindowBeInvisible(fake_aw_contents); |
| EXPECT_TRUE(callback()->called()); |
| } |
| |
| } // namespace android_webview |