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,
bool visible) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
browser_view_renderer_.SetWindowVisibility(visible);
if (visible)
AwContentsLifecycleNotifier::GetInstance().OnWebViewWindowBeVisible(this);
else
AwContentsLifecycleNotifier::GetInstance().OnWebViewWindowBeInvisible(this);
}
void AwContents::SetIsPaused(JNIEnv* env,
......@@ -958,12 +962,14 @@ void AwContents::OnAttachedToWindow(JNIEnv* env,
int h) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
browser_view_renderer_.OnAttachedToWindow(w, h);
AwContentsLifecycleNotifier::GetInstance().OnWebViewAttachedToWindow(this);
}
void AwContents::OnDetachedFromWindow(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
browser_view_renderer_.OnDetachedFromWindow();
AwContentsLifecycleNotifier::GetInstance().OnWebViewDetachedFromWindow(this);
}
bool AwContents::IsVisible(JNIEnv* env, const JavaParamRef<jobject>& obj) {
......
......@@ -14,6 +14,23 @@ using content::BrowserThread;
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
AwContentsLifecycleNotifier& AwContentsLifecycleNotifier::GetInstance() {
static base::NoDestructor<AwContentsLifecycleNotifier> instance;
......@@ -26,10 +43,10 @@ void AwContentsLifecycleNotifier::OnWebViewCreated(
has_aw_contents_ever_created_ = true;
uint64_t id = reinterpret_cast<uint64_t>(aw_contents);
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(
std::make_pair(id, AwContentsState::kDetached));
aw_contents_id_to_data_.insert(
std::make_pair(id, AwContentsLifecycleNotifier::AwContentsData()));
state_count_[ToIndex(AwContentsState::kDetached)]++;
UpdateAppState();
......@@ -43,12 +60,12 @@ void AwContentsLifecycleNotifier::OnWebViewDestroyed(
const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
uint64_t id = reinterpret_cast<uint64_t>(aw_contents);
const auto it = aw_contents_id_to_state_.find(id);
DCHECK(it != aw_contents_id_to_state_.end());
const auto it = aw_contents_id_to_data_.find(id);
DCHECK(it != aw_contents_id_to_data_.end());
state_count_[ToIndex(it->second)]--;
DCHECK(state_count_[ToIndex(it->second)] >= 0);
aw_contents_id_to_state_.erase(it);
state_count_[ToIndex(it->second.aw_content_state)]--;
DCHECK(state_count_[ToIndex(it->second.aw_content_state)] >= 0);
aw_contents_id_to_data_.erase(it);
UpdateAppState();
if (!HasAwContentsInstance()) {
......@@ -60,25 +77,34 @@ void AwContentsLifecycleNotifier::OnWebViewDestroyed(
void AwContentsLifecycleNotifier::OnWebViewAttachedToWindow(
const AwContents* aw_contents) {
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(
const AwContents* aw_contents) {
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(
const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
OnAwContentsStateChanged(aw_contents, AwContentsState::kForeground);
auto* data = GetAwContentsData(aw_contents);
data->window_visible = true;
OnAwContentsStateChanged(data);
}
void AwContentsLifecycleNotifier::OnWebViewWindowBeInvisible(
const AwContents* aw_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
OnAwContentsStateChanged(aw_contents, AwContentsState::kBackground);
auto* data = GetAwContentsData(aw_contents);
data->window_visible = false;
OnAwContentsStateChanged(data);
}
void AwContentsLifecycleNotifier::AddObserver(
......@@ -105,16 +131,15 @@ size_t AwContentsLifecycleNotifier::ToIndex(AwContentsState state) const {
}
void AwContentsLifecycleNotifier::OnAwContentsStateChanged(
const AwContents* aw_contents,
AwContentsState state) {
uint64_t id = reinterpret_cast<uint64_t>(aw_contents);
const auto it = aw_contents_id_to_state_.find(id);
DCHECK(it != aw_contents_id_to_state_.end());
DCHECK(it->second != state);
state_count_[ToIndex(it->second)]--;
DCHECK(state_count_[ToIndex(it->second)] >= 0);
AwContentsLifecycleNotifier::AwContentsData* data) {
AwContentsLifecycleNotifier::AwContentsState state =
CalcuateState(data->attached_to_window, data->window_visible);
if (data->aw_content_state == state)
return;
state_count_[ToIndex(data->aw_content_state)]--;
DCHECK(state_count_[ToIndex(data->aw_content_state)] >= 0);
state_count_[ToIndex(state)]++;
aw_contents_id_to_state_[it->first] = state;
data->aw_content_state = state;
UpdateAppState();
}
......@@ -144,4 +169,11 @@ bool AwContentsLifecycleNotifier::HasAwContentsInstance() const {
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
......@@ -6,7 +6,6 @@
#define ANDROID_WEBVIEW_BROWSER_AW_CONTENTS_LIFECYCLE_NOTIFIER_H_
#include <map>
#include <set>
#include "android_webview/browser/webview_app_state_observer.h"
#include "base/android/jni_android.h"
......@@ -46,6 +45,12 @@ class AwContentsLifecycleNotifier {
}
private:
struct AwContentsData {
bool attached_to_window = false;
bool window_visible = false;
AwContentsState aw_content_state = AwContentsState::kDetached;
};
friend base::NoDestructor<AwContentsLifecycleNotifier>;
friend class TestAwContentsLifecycleNotifier;
......@@ -53,14 +58,19 @@ class AwContentsLifecycleNotifier {
virtual ~AwContentsLifecycleNotifier();
size_t ToIndex(AwContentsState state) const;
void OnAwContentsStateChanged(const AwContents* aw_contents,
AwContentsState state);
void OnAwContentsStateChanged(
AwContentsLifecycleNotifier::AwContentsData* data);
void UpdateAppState();
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.
int state_count_[3]{};
......
......@@ -58,12 +58,12 @@ class AwContentsLifecycleNotifierTest : public testing::Test {
AwContentsLifecycleNotifier* notifier() { return notifier_.get(); }
void VerifyAwContentsStateCount(size_t unknown_count,
void VerifyAwContentsStateCount(size_t detached_count,
size_t foreground_count,
size_t background_count) {
ASSERT_EQ(GetAwContentsStateCount(
AwContentsLifecycleNotifier::AwContentsState::kDetached),
unknown_count);
detached_count);
ASSERT_EQ(GetAwContentsStateCount(
AwContentsLifecycleNotifier::AwContentsState::kForeground),
foreground_count);
......@@ -211,4 +211,51 @@ TEST_F(AwContentsLifecycleNotifierTest, MultipleAwContents) {
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
......@@ -12,7 +12,7 @@ namespace android_webview {
class WebViewAppStateObserver {
public:
enum class State {
// All WebViews are in unknown state.
// All WebViews are detached from window.
kUnknown,
// At least one WebView is foreground.
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