Commit f1b0cfbf authored by Tao Bai's avatar Tao Bai Committed by Commit Bot

Observe the app state - part 2: Add call site

Add call site for AwContents state of AwContentsLifecycleNotifier.

Bug: 1042048
Change-Id: I6d59b2b956fe8b1dbf2f208a3f2881fdc6f4d778
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2023400
Commit-Queue: Tao Bai <michaelbai@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737229}
parent 2215769d
...@@ -943,6 +943,10 @@ void AwContents::SetWindowVisibility(JNIEnv* env, ...@@ -943,6 +943,10 @@ void AwContents::SetWindowVisibility(JNIEnv* env,
bool visible) { bool visible) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
browser_view_renderer_.SetWindowVisibility(visible); browser_view_renderer_.SetWindowVisibility(visible);
if (visible)
AwContentsLifecycleNotifier::GetInstance().OnWebViewWindowBeVisible(this);
else
AwContentsLifecycleNotifier::GetInstance().OnWebViewWindowBeInvisible(this);
} }
void AwContents::SetIsPaused(JNIEnv* env, void AwContents::SetIsPaused(JNIEnv* env,
...@@ -958,12 +962,14 @@ void AwContents::OnAttachedToWindow(JNIEnv* env, ...@@ -958,12 +962,14 @@ void AwContents::OnAttachedToWindow(JNIEnv* env,
int h) { int h) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
browser_view_renderer_.OnAttachedToWindow(w, h); browser_view_renderer_.OnAttachedToWindow(w, h);
AwContentsLifecycleNotifier::GetInstance().OnWebViewAttachedToWindow(this);
} }
void AwContents::OnDetachedFromWindow(JNIEnv* env, void AwContents::OnDetachedFromWindow(JNIEnv* env,
const JavaParamRef<jobject>& obj) { const JavaParamRef<jobject>& obj) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
browser_view_renderer_.OnDetachedFromWindow(); browser_view_renderer_.OnDetachedFromWindow();
AwContentsLifecycleNotifier::GetInstance().OnWebViewDetachedFromWindow(this);
} }
bool AwContents::IsVisible(JNIEnv* env, const JavaParamRef<jobject>& obj) { bool AwContents::IsVisible(JNIEnv* env, const JavaParamRef<jobject>& obj) {
......
...@@ -14,6 +14,23 @@ using content::BrowserThread; ...@@ -14,6 +14,23 @@ using content::BrowserThread;
namespace android_webview { namespace android_webview {
namespace {
AwContentsLifecycleNotifier::AwContentsState CalcuateState(
bool is_atached_to_window,
bool is_window_visible) {
// Can't assume the sequence of Attached, Detached, Visible, Invisible event
// because the app could changed it; Calculate the state here.
if (is_atached_to_window) {
return is_window_visible
? AwContentsLifecycleNotifier::AwContentsState::kForeground
: AwContentsLifecycleNotifier::AwContentsState::kBackground;
}
return AwContentsLifecycleNotifier::AwContentsState::kDetached;
}
} // namespace
// static // static
AwContentsLifecycleNotifier& AwContentsLifecycleNotifier::GetInstance() { AwContentsLifecycleNotifier& AwContentsLifecycleNotifier::GetInstance() {
static base::NoDestructor<AwContentsLifecycleNotifier> instance; static base::NoDestructor<AwContentsLifecycleNotifier> instance;
...@@ -26,10 +43,10 @@ void AwContentsLifecycleNotifier::OnWebViewCreated( ...@@ -26,10 +43,10 @@ void AwContentsLifecycleNotifier::OnWebViewCreated(
has_aw_contents_ever_created_ = true; has_aw_contents_ever_created_ = true;
uint64_t id = reinterpret_cast<uint64_t>(aw_contents); uint64_t id = reinterpret_cast<uint64_t>(aw_contents);
bool first_created = !HasAwContentsInstance(); bool first_created = !HasAwContentsInstance();
DCHECK(aw_contents_id_to_state_.find(id) == aw_contents_id_to_state_.end()); DCHECK(aw_contents_id_to_data_.find(id) == aw_contents_id_to_data_.end());
aw_contents_id_to_state_.insert( aw_contents_id_to_data_.insert(
std::make_pair(id, AwContentsState::kDetached)); std::make_pair(id, AwContentsLifecycleNotifier::AwContentsData()));
state_count_[ToIndex(AwContentsState::kDetached)]++; state_count_[ToIndex(AwContentsState::kDetached)]++;
UpdateAppState(); UpdateAppState();
...@@ -43,12 +60,12 @@ void AwContentsLifecycleNotifier::OnWebViewDestroyed( ...@@ -43,12 +60,12 @@ void AwContentsLifecycleNotifier::OnWebViewDestroyed(
const AwContents* aw_contents) { const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
uint64_t id = reinterpret_cast<uint64_t>(aw_contents); uint64_t id = reinterpret_cast<uint64_t>(aw_contents);
const auto it = aw_contents_id_to_state_.find(id); const auto it = aw_contents_id_to_data_.find(id);
DCHECK(it != aw_contents_id_to_state_.end()); DCHECK(it != aw_contents_id_to_data_.end());
state_count_[ToIndex(it->second)]--; state_count_[ToIndex(it->second.aw_content_state)]--;
DCHECK(state_count_[ToIndex(it->second)] >= 0); DCHECK(state_count_[ToIndex(it->second.aw_content_state)] >= 0);
aw_contents_id_to_state_.erase(it); aw_contents_id_to_data_.erase(it);
UpdateAppState(); UpdateAppState();
if (!HasAwContentsInstance()) { if (!HasAwContentsInstance()) {
...@@ -60,25 +77,34 @@ void AwContentsLifecycleNotifier::OnWebViewDestroyed( ...@@ -60,25 +77,34 @@ void AwContentsLifecycleNotifier::OnWebViewDestroyed(
void AwContentsLifecycleNotifier::OnWebViewAttachedToWindow( void AwContentsLifecycleNotifier::OnWebViewAttachedToWindow(
const AwContents* aw_contents) { const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
OnAwContentsStateChanged(aw_contents, AwContentsState::kBackground); auto* data = GetAwContentsData(aw_contents);
data->attached_to_window = true;
OnAwContentsStateChanged(data);
} }
void AwContentsLifecycleNotifier::OnWebViewDetachedFromWindow( void AwContentsLifecycleNotifier::OnWebViewDetachedFromWindow(
const AwContents* aw_contents) { const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
OnAwContentsStateChanged(aw_contents, AwContentsState::kDetached); auto* data = GetAwContentsData(aw_contents);
data->attached_to_window = false;
DCHECK(data->aw_content_state != AwContentsState::kDetached);
OnAwContentsStateChanged(data);
} }
void AwContentsLifecycleNotifier::OnWebViewWindowBeVisible( void AwContentsLifecycleNotifier::OnWebViewWindowBeVisible(
const AwContents* aw_contents) { const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
OnAwContentsStateChanged(aw_contents, AwContentsState::kForeground); auto* data = GetAwContentsData(aw_contents);
data->window_visible = true;
OnAwContentsStateChanged(data);
} }
void AwContentsLifecycleNotifier::OnWebViewWindowBeInvisible( void AwContentsLifecycleNotifier::OnWebViewWindowBeInvisible(
const AwContents* aw_contents) { const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
OnAwContentsStateChanged(aw_contents, AwContentsState::kBackground); auto* data = GetAwContentsData(aw_contents);
data->window_visible = false;
OnAwContentsStateChanged(data);
} }
void AwContentsLifecycleNotifier::AddObserver( void AwContentsLifecycleNotifier::AddObserver(
...@@ -105,16 +131,15 @@ size_t AwContentsLifecycleNotifier::ToIndex(AwContentsState state) const { ...@@ -105,16 +131,15 @@ size_t AwContentsLifecycleNotifier::ToIndex(AwContentsState state) const {
} }
void AwContentsLifecycleNotifier::OnAwContentsStateChanged( void AwContentsLifecycleNotifier::OnAwContentsStateChanged(
const AwContents* aw_contents, AwContentsLifecycleNotifier::AwContentsData* data) {
AwContentsState state) { AwContentsLifecycleNotifier::AwContentsState state =
uint64_t id = reinterpret_cast<uint64_t>(aw_contents); CalcuateState(data->attached_to_window, data->window_visible);
const auto it = aw_contents_id_to_state_.find(id); if (data->aw_content_state == state)
DCHECK(it != aw_contents_id_to_state_.end()); return;
DCHECK(it->second != state); state_count_[ToIndex(data->aw_content_state)]--;
state_count_[ToIndex(it->second)]--; DCHECK(state_count_[ToIndex(data->aw_content_state)] >= 0);
DCHECK(state_count_[ToIndex(it->second)] >= 0);
state_count_[ToIndex(state)]++; state_count_[ToIndex(state)]++;
aw_contents_id_to_state_[it->first] = state; data->aw_content_state = state;
UpdateAppState(); UpdateAppState();
} }
...@@ -144,4 +169,11 @@ bool AwContentsLifecycleNotifier::HasAwContentsInstance() const { ...@@ -144,4 +169,11 @@ bool AwContentsLifecycleNotifier::HasAwContentsInstance() const {
return false; return false;
} }
AwContentsLifecycleNotifier::AwContentsData*
AwContentsLifecycleNotifier::GetAwContentsData(const AwContents* aw_contents) {
uint64_t id = reinterpret_cast<uint64_t>(aw_contents);
DCHECK(aw_contents_id_to_data_.find(id) != aw_contents_id_to_data_.end());
return &aw_contents_id_to_data_.at(id);
}
} // namespace android_webview } // namespace android_webview
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#define ANDROID_WEBVIEW_BROWSER_AW_CONTENTS_LIFECYCLE_NOTIFIER_H_ #define ANDROID_WEBVIEW_BROWSER_AW_CONTENTS_LIFECYCLE_NOTIFIER_H_
#include <map> #include <map>
#include <set>
#include "android_webview/browser/webview_app_state_observer.h" #include "android_webview/browser/webview_app_state_observer.h"
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
...@@ -46,6 +45,12 @@ class AwContentsLifecycleNotifier { ...@@ -46,6 +45,12 @@ class AwContentsLifecycleNotifier {
} }
private: private:
struct AwContentsData {
bool attached_to_window = false;
bool window_visible = false;
AwContentsState aw_content_state = AwContentsState::kDetached;
};
friend base::NoDestructor<AwContentsLifecycleNotifier>; friend base::NoDestructor<AwContentsLifecycleNotifier>;
friend class TestAwContentsLifecycleNotifier; friend class TestAwContentsLifecycleNotifier;
...@@ -53,14 +58,19 @@ class AwContentsLifecycleNotifier { ...@@ -53,14 +58,19 @@ class AwContentsLifecycleNotifier {
virtual ~AwContentsLifecycleNotifier(); virtual ~AwContentsLifecycleNotifier();
size_t ToIndex(AwContentsState state) const; size_t ToIndex(AwContentsState state) const;
void OnAwContentsStateChanged(const AwContents* aw_contents, void OnAwContentsStateChanged(
AwContentsState state); AwContentsLifecycleNotifier::AwContentsData* data);
void UpdateAppState(); void UpdateAppState();
bool HasAwContentsInstance() const; bool HasAwContentsInstance() const;
// The AwContentId to AwContentState mapping.
std::map<uint64_t, AwContentsState> aw_contents_id_to_state_; AwContentsLifecycleNotifier::AwContentsData* GetAwContentsData(
const AwContents* aw_contents);
// The AwContentId to AwContentsData mapping.
std::map<uint64_t, AwContentsLifecycleNotifier::AwContentsData>
aw_contents_id_to_data_;
// The number of AwContents instances in each AwContentsState. // The number of AwContents instances in each AwContentsState.
int state_count_[3]{}; int state_count_[3]{};
......
...@@ -58,12 +58,12 @@ class AwContentsLifecycleNotifierTest : public testing::Test { ...@@ -58,12 +58,12 @@ class AwContentsLifecycleNotifierTest : public testing::Test {
AwContentsLifecycleNotifier* notifier() { return notifier_.get(); } AwContentsLifecycleNotifier* notifier() { return notifier_.get(); }
void VerifyAwContentsStateCount(size_t unknown_count, void VerifyAwContentsStateCount(size_t detached_count,
size_t foreground_count, size_t foreground_count,
size_t background_count) { size_t background_count) {
ASSERT_EQ(GetAwContentsStateCount( ASSERT_EQ(GetAwContentsStateCount(
AwContentsLifecycleNotifier::AwContentsState::kDetached), AwContentsLifecycleNotifier::AwContentsState::kDetached),
unknown_count); detached_count);
ASSERT_EQ(GetAwContentsStateCount( ASSERT_EQ(GetAwContentsStateCount(
AwContentsLifecycleNotifier::AwContentsState::kForeground), AwContentsLifecycleNotifier::AwContentsState::kForeground),
foreground_count); foreground_count);
...@@ -211,4 +211,51 @@ TEST_F(AwContentsLifecycleNotifierTest, MultipleAwContents) { ...@@ -211,4 +211,51 @@ TEST_F(AwContentsLifecycleNotifierTest, MultipleAwContents) {
ASSERT_TRUE(HasAwContentsEverCreated()); 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());
}
} // namespace android_webview } // namespace android_webview
...@@ -12,7 +12,7 @@ namespace android_webview { ...@@ -12,7 +12,7 @@ namespace android_webview {
class WebViewAppStateObserver { class WebViewAppStateObserver {
public: public:
enum class State { enum class State {
// All WebViews are in unknown state. // All WebViews are detached from window.
kUnknown, kUnknown,
// At least one WebView is foreground. // At least one WebView is foreground.
kForeground, kForeground,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment