Commit 9c9316dc authored by Scott Haseley's avatar Scott Haseley Committed by Commit Bot

Display Page Lifecycle State in chrome://discards

In the Lifecycle State column, we were showing internal
TabLifecycleUnitStates rather than Page Lifecycle
states. As a result, we were showing "active" for the active, hidden,
and passive page lifecycle states. As chrome://discards will be used by
external developers, we should show Page Lifecycles States from the
spec (https://github.com/WICG/page-lifecycle).

This CL plumbs focus to the discards UI and uses the 3-tuple of
(internal_tab_state, visibility, focus) to compute the page lifecycle
state. Internal states are still shown in parentheses to aid in
TabManager debugging, e.g. "hidden (pending frozen)".

Bug: 856321
Cq-Include-Trybots: luci.chromium.try:closure_compilation
Change-Id: Ieb6f0a098cc165c0b7544b3f27a7317f8c78caa1
Reviewed-on: https://chromium-review.googlesource.com/1114282
Commit-Queue: Scott Haseley <shaseley@google.com>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Reviewed-by: default avatarShubhie Panicker <panicker@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585560}
parent 89b2ab63
...@@ -217,11 +217,11 @@ cr.define('discards', function() { ...@@ -217,11 +217,11 @@ cr.define('discards', function() {
*/ */
function visibilityToString(visibility) { function visibilityToString(visibility) {
switch (visibility) { switch (visibility) {
case 0: case mojom.LifecycleUnitVisibility.HIDDEN:
return 'hidden'; return 'hidden';
case 1: case mojom.LifecycleUnitVisibility.OCCLUDED:
return 'occluded'; return 'occluded';
case 2: case mojom.LifecycleUnitVisibility.VISIBLE:
return 'visible'; return 'visible';
} }
assertNotReached('Unknown visibility: ' + visibility); assertNotReached('Unknown visibility: ' + visibility);
...@@ -235,11 +235,11 @@ cr.define('discards', function() { ...@@ -235,11 +235,11 @@ cr.define('discards', function() {
*/ */
function loadingStateToString(loadingState) { function loadingStateToString(loadingState) {
switch (loadingState) { switch (loadingState) {
case 0: case mojom.LifecycleUnitLoadingState.UNLOADED:
return 'unloaded'; return 'unloaded';
case 1: case mojom.LifecycleUnitLoadingState.LOADING:
return 'loading'; return 'loading';
case 2: case mojom.LifecycleUnitLoadingState.LOADED:
return 'loaded'; return 'loaded';
} }
assertNotReached('Unknown loadingState: ' + loadingState); assertNotReached('Unknown loadingState: ' + loadingState);
...@@ -248,7 +248,7 @@ cr.define('discards', function() { ...@@ -248,7 +248,7 @@ cr.define('discards', function() {
/** /**
* Returns a string representation of a discard reason. * Returns a string representation of a discard reason.
* @param {mojom.LifecycleUnitDiscardReason} reason The discard reason. * @param {mojom.LifecycleUnitDiscardReason} reason The discard reason.
* @param {string} A string representation of the discarding reason. * @return {string} A string representation of the discarding reason.
*/ */
function discardReasonToString(reason) { function discardReasonToString(reason) {
switch (reason) { switch (reason) {
...@@ -267,25 +267,40 @@ cr.define('discards', function() { ...@@ -267,25 +267,40 @@ cr.define('discards', function() {
* @param {mojom.LifecycleUnitState} state The lifecycle state. * @param {mojom.LifecycleUnitState} state The lifecycle state.
* @param {mojom.LifecycleUnitDiscardReason} reason The discard reason. This * @param {mojom.LifecycleUnitDiscardReason} reason The discard reason. This
* is only used if the state is discard related. * is only used if the state is discard related.
* @param {string} A string representation of the lifecycle state, augmented * @param {int} visibility A value in LifecycleUnitVisibility.
* @param {boolean} hasFocus Whether or not the tab has input focus.
* @return {string} A string representation of the lifecycle state, augmented
* with the discard reason if appropriate. * with the discard reason if appropriate.
*/ */
function lifecycleStateToString(state, reason) { function lifecycleStateToString(state, reason, visibility, hasFocus) {
let pageLifecycleStateFromVisibilityAndFocus = function() {
switch (visibility) {
case mojom.LifecycleUnitVisibility.HIDDEN:
case mojom.LifecycleUnitVisibility.OCCLUDED:
// An occluded page is also considered hidden.
return 'hidden';
case mojom.LifecycleUnitVisibility.VISIBLE:
return hasFocus ? 'active' : 'passive';
}
assertNotReached('Unknown visibility: ' + visibility);
};
switch (state) { switch (state) {
case mojom.LifecycleUnitState.ACTIVE: case mojom.LifecycleUnitState.ACTIVE:
return 'active'; return pageLifecycleStateFromVisibilityAndFocus();
case mojom.LifecycleUnitState.THROTTLED: case mojom.LifecycleUnitState.THROTTLED:
return 'throttled'; return pageLifecycleStateFromVisibilityAndFocus() + ' (throttled)';
case mojom.LifecycleUnitState.PENDING_FREEZE: case mojom.LifecycleUnitState.PENDING_FREEZE:
return 'pending frozen'; return pageLifecycleStateFromVisibilityAndFocus() + ' (pending frozen)';
case mojom.LifecycleUnitState.FROZEN: case mojom.LifecycleUnitState.FROZEN:
return 'frozen'; return 'frozen';
case mojom.LifecycleUnitState.PENDING_DISCARD: case mojom.LifecycleUnitState.PENDING_DISCARD:
return 'pending discard (' + discardReasonToString(reason) + ')'; return pageLifecycleStateFromVisibilityAndFocus() +
' (pending discard (' + discardReasonToString(reason) + '))';
case mojom.LifecycleUnitState.DISCARDED: case mojom.LifecycleUnitState.DISCARDED:
return 'discarded (' + discardReasonToString(reason) + ')'; return 'discarded (' + discardReasonToString(reason) + ')';
case mojom.LifecycleUnitState.PENDING_UNFREEZE: case mojom.LifecycleUnitState.PENDING_UNFREEZE:
return 'pending unfreeze'; return 'frozen (pending unfreeze)';
} }
assertNotReached('Unknown lifecycle state: ' + state); assertNotReached('Unknown lifecycle state: ' + state);
} }
...@@ -401,7 +416,8 @@ cr.define('discards', function() { ...@@ -401,7 +416,8 @@ cr.define('discards', function() {
row.querySelector('.state-cell').textContent = row.querySelector('.state-cell').textContent =
(info.loadingState != mojom.LifecycleUnitLoadingState.UNLOADED || (info.loadingState != mojom.LifecycleUnitLoadingState.UNLOADED ||
info.discardCount > 0) ? info.discardCount > 0) ?
lifecycleStateToString(info.state, info.discardReason) : lifecycleStateToString(
info.state, info.discardReason, info.visibility, info.hasFocus) :
''; '';
row.querySelector('.discard-count-cell').textContent = row.querySelector('.discard-count-cell').textContent =
info.discardCount.toString(); info.discardCount.toString();
...@@ -437,7 +453,9 @@ cr.define('discards', function() { ...@@ -437,7 +453,9 @@ cr.define('discards', function() {
let discardUrgentEnabled = false; let discardUrgentEnabled = false;
if (info.loadingState == mojom.LifecycleUnitLoadingState.UNLOADED) { if (info.loadingState == mojom.LifecycleUnitLoadingState.UNLOADED) {
loadEnabled = true; loadEnabled = true;
} else if (info.visibility == 0) { } else if (
info.visibility == mojom.LifecycleUnitVisibility.HIDDEN ||
info.visibility == mojom.LifecycleUnitVisibility.OCCLUDED) {
// Only tabs that aren't visible can be frozen or discarded for now. // Only tabs that aren't visible can be frozen or discarded for now.
freezeEnabled = true; freezeEnabled = true;
discardEnabled = true; discardEnabled = true;
......
...@@ -58,6 +58,8 @@ struct TabDiscardsInfo { ...@@ -58,6 +58,8 @@ struct TabDiscardsInfo {
double reactivation_score; double reactivation_score;
// Site engagement score. // Site engagement score.
double site_engagement_score; double site_engagement_score;
// Whether or not the tab has input focus.
bool has_focus;
}; };
// Interface for providing information about discards. Lives in the browser // Interface for providing information about discards. Lives in the browser
......
...@@ -150,6 +150,11 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider { ...@@ -150,6 +150,11 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider {
if (info->has_reactivation_score) if (info->has_reactivation_score)
info->reactivation_score = reactivation_score.value(); info->reactivation_score = reactivation_score.value();
info->site_engagement_score = GetSiteEngagementScore(contents); info->site_engagement_score = GetSiteEngagementScore(contents);
// TODO(crbug.com/876340): The focus is used to compute the page lifecycle
// state. This should be replaced with the actual page lifecycle state
// information from Blink, but this depends on implementing the passive
// state and plumbing it to the browser.
info->has_focus = lifecycle_unit->GetLastFocusedTime().is_max();
infos.push_back(std::move(info)); infos.push_back(std::move(info));
} }
......
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