Commit 95c0df9a authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

Add loading state / actions to chrome://discards web UI.

Cq-Include-Trybots: luci.chromium.try:closure_compilation
Change-Id: I3c25b29dfbb00edbb179003d17cfe061a32ebce3
Reviewed-on: https://chromium-review.googlesource.com/1100086
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568099}
parent a65fc33c
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
namespace resource_coordinator { namespace resource_coordinator {
using ::mojom::LifecycleUnitLoadingState;
using ::mojom::LifecycleUnitState; using ::mojom::LifecycleUnitState;
class DecisionDetails; class DecisionDetails;
...@@ -91,6 +92,9 @@ class LifecycleUnit { ...@@ -91,6 +92,9 @@ class LifecycleUnit {
// base::TimeTicks::Max() if the LifecycleUnit is currently visible. // base::TimeTicks::Max() if the LifecycleUnit is currently visible.
virtual base::TimeTicks GetLastVisibleTime() const = 0; virtual base::TimeTicks GetLastVisibleTime() const = 0;
// Returns the loading state associated with a LifecycleUnit.
virtual LifecycleUnitLoadingState GetLoadingState() const = 0;
// Returns the process hosting this LifecycleUnit. Used to distribute OOM // Returns the process hosting this LifecycleUnit. Used to distribute OOM
// scores. // scores.
// //
...@@ -112,6 +116,10 @@ class LifecycleUnit { ...@@ -112,6 +116,10 @@ class LifecycleUnit {
// Returns the current state of this LifecycleUnit. // Returns the current state of this LifecycleUnit.
virtual LifecycleUnitState GetState() const = 0; virtual LifecycleUnitState GetState() const = 0;
// Request that the LifecycleUnit be loaded, return true if the request is
// successful.
virtual bool Load() = 0;
// Request that the LifecycleUnit be frozen, return true if the request is // Request that the LifecycleUnit be frozen, return true if the request is
// successfully sent. // successfully sent.
virtual bool Freeze() = 0; virtual bool Freeze() = 0;
......
...@@ -193,12 +193,8 @@ void TabLifecycleUnitSource::TabLifecycleUnit::SetFocused(bool focused) { ...@@ -193,12 +193,8 @@ void TabLifecycleUnitSource::TabLifecycleUnit::SetFocused(bool focused) {
// Reload the tab. // Reload the tab.
SetState(LifecycleUnitState::ACTIVE, SetState(LifecycleUnitState::ACTIVE,
StateChangeReason::BROWSER_INITIATED); StateChangeReason::BROWSER_INITIATED);
// See comment in Discard() for an explanation of why "needs reload" is bool loaded = Load();
// false when a tab is discarded. DCHECK(loaded);
// TODO(fdoray): Remove NavigationControllerImpl::needs_reload_ once
// session restore is handled by LifecycleManager.
GetWebContents()->GetController().SetNeedsReload();
GetWebContents()->GetController().LoadIfNecessary();
break; break;
} }
...@@ -311,6 +307,27 @@ content::Visibility TabLifecycleUnitSource::TabLifecycleUnit::GetVisibility() ...@@ -311,6 +307,27 @@ content::Visibility TabLifecycleUnitSource::TabLifecycleUnit::GetVisibility()
return GetWebContents()->GetVisibility(); return GetWebContents()->GetVisibility();
} }
LifecycleUnitLoadingState
TabLifecycleUnitSource::TabLifecycleUnit::GetLoadingState() const {
return TabLoadTracker::Get()->GetLoadingState(GetWebContents());
}
bool TabLifecycleUnitSource::TabLifecycleUnit::Load() {
if (GetLoadingState() != LifecycleUnitLoadingState::UNLOADED)
return false;
// TODO(chrisha): Make this work more elegantly in the case of background tab
// loading as well, which uses a NavigationThrottle that can be released.
// See comment in Discard() for an explanation of why "needs reload" is
// false when a tab is discarded.
// TODO(fdoray): Remove NavigationControllerImpl::needs_reload_ once
// session restore is handled by LifecycleManager.
GetWebContents()->GetController().SetNeedsReload();
GetWebContents()->GetController().LoadIfNecessary();
return true;
}
bool TabLifecycleUnitSource::TabLifecycleUnit::Freeze() { bool TabLifecycleUnitSource::TabLifecycleUnit::Freeze() {
if (!IsValidStateChange(GetState(), LifecycleUnitState::PENDING_FREEZE, if (!IsValidStateChange(GetState(), LifecycleUnitState::PENDING_FREEZE,
StateChangeReason::BROWSER_INITIATED)) { StateChangeReason::BROWSER_INITIATED)) {
...@@ -543,6 +560,13 @@ void TabLifecycleUnitSource::TabLifecycleUnit::FinishDiscard( ...@@ -543,6 +560,13 @@ void TabLifecycleUnitSource::TabLifecycleUnit::FinishDiscard(
std::unique_ptr<content::WebContents> null_contents = std::unique_ptr<content::WebContents> null_contents =
content::WebContents::Create(create_params); content::WebContents::Create(create_params);
content::WebContents* raw_null_contents = null_contents.get(); content::WebContents* raw_null_contents = null_contents.get();
// Attach the ResourceCoordinatorTabHelper. In production code this has
// already been attached by now due to AttachTabHelpers, but there's a long
// tail of tests that don't add these helpers. This ensures that the various
// DCHECKs in the state transition machinery don't fail.
ResourceCoordinatorTabHelper::CreateForWebContents(raw_null_contents);
// Copy over the state from the navigation controller to preserve the // Copy over the state from the navigation controller to preserve the
// back/forward history and to continue to display the correct title/favicon. // back/forward history and to continue to display the correct title/favicon.
// //
...@@ -599,6 +623,7 @@ void TabLifecycleUnitSource::TabLifecycleUnit::FinishDiscard( ...@@ -599,6 +623,7 @@ void TabLifecycleUnitSource::TabLifecycleUnit::FinishDiscard(
SetState(LifecycleUnitState::DISCARDED, SetState(LifecycleUnitState::DISCARDED,
DiscardReasonToStateChangeReason(discard_reason)); DiscardReasonToStateChangeReason(discard_reason));
++discard_count_; ++discard_count_;
DCHECK_EQ(GetLoadingState(), LifecycleUnitLoadingState::UNLOADED);
} }
content::WebContents* TabLifecycleUnitSource::TabLifecycleUnit::GetWebContents() content::WebContents* TabLifecycleUnitSource::TabLifecycleUnit::GetWebContents()
......
...@@ -89,6 +89,8 @@ class TabLifecycleUnitSource::TabLifecycleUnit ...@@ -89,6 +89,8 @@ class TabLifecycleUnitSource::TabLifecycleUnit
base::ProcessHandle GetProcessHandle() const override; base::ProcessHandle GetProcessHandle() const override;
SortKey GetSortKey() const override; SortKey GetSortKey() const override;
content::Visibility GetVisibility() const override; content::Visibility GetVisibility() const override;
LifecycleUnitLoadingState GetLoadingState() const override;
bool Load() override;
bool Freeze() override; bool Freeze() override;
int GetEstimatedMemoryFreedOnDiscardKB() const override; int GetEstimatedMemoryFreedOnDiscardKB() const override;
bool CanPurge() const override; bool CanPurge() const override;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h" #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h" #include "chrome/browser/resource_coordinator/lifecycle_unit_source_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_observer.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h" #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
#include "chrome/browser/resource_coordinator/test_lifecycle_unit.h" #include "chrome/browser/resource_coordinator/test_lifecycle_unit.h"
...@@ -247,6 +248,8 @@ class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness { ...@@ -247,6 +248,8 @@ class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness {
EXPECT_EQ(LifecycleUnitState::DISCARDED, lifecycle_unit->GetState()); EXPECT_EQ(LifecycleUnitState::DISCARDED, lifecycle_unit->GetState());
} }
void DiscardAndAttachTabHelpers(LifecycleUnit* lifecycle_unit) {}
void DetachWebContentsTest(DiscardReason reason) { void DetachWebContentsTest(DiscardReason reason) {
LifecycleUnit* first_lifecycle_unit = nullptr; LifecycleUnit* first_lifecycle_unit = nullptr;
LifecycleUnit* second_lifecycle_unit = nullptr; LifecycleUnit* second_lifecycle_unit = nullptr;
...@@ -454,6 +457,9 @@ class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness { ...@@ -454,6 +457,9 @@ class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness {
std::unique_ptr<content::WebContents> CreateAndNavigateWebContents() { std::unique_ptr<content::WebContents> CreateAndNavigateWebContents() {
std::unique_ptr<content::WebContents> web_contents = std::unique_ptr<content::WebContents> web_contents =
CreateTestWebContents(); CreateTestWebContents();
// Attach the RC tab helper. In production code the browser
// WebContentsDelegate takes care of this.
ResourceCoordinatorTabHelper::CreateForWebContents(web_contents.get());
// Commit an URL to allow discarding. // Commit an URL to allow discarding.
content::WebContentsTester::For(web_contents.get()) content::WebContentsTester::For(web_contents.get())
->NavigateAndCommit(GURL("https://www.example.com")); ->NavigateAndCommit(GURL("https://www.example.com"));
...@@ -649,6 +655,8 @@ TEST_F(TabLifecycleUnitSourceTest, CannotFreezeADiscardedTab) { ...@@ -649,6 +655,8 @@ TEST_F(TabLifecycleUnitSourceTest, CannotFreezeADiscardedTab) {
background_lifecycle_unit->Discard(DiscardReason::kUrgent); background_lifecycle_unit->Discard(DiscardReason::kUrgent);
testing::Mock::VerifyAndClear(&tab_observer_); testing::Mock::VerifyAndClear(&tab_observer_);
TransitionFromPendingDiscardToDiscardedIfNeeded(DiscardReason::kUrgent,
background_lifecycle_unit);
EXPECT_EQ(LifecycleUnitState::DISCARDED, EXPECT_EQ(LifecycleUnitState::DISCARDED,
background_lifecycle_unit->GetState()); background_lifecycle_unit->GetState());
EXPECT_NE(initial_web_contents, tab_strip_model_->GetWebContentsAt(0)); EXPECT_NE(initial_web_contents, tab_strip_model_->GetWebContentsAt(0));
...@@ -707,7 +715,8 @@ TEST_F(TabLifecycleUnitSourceTest, TabProactiveDiscardedByFrozenTimeout) { ...@@ -707,7 +715,8 @@ TEST_F(TabLifecycleUnitSourceTest, TabProactiveDiscardedByFrozenTimeout) {
background_lifecycle_unit->Discard(DiscardReason::kProactive); background_lifecycle_unit->Discard(DiscardReason::kProactive);
EXPECT_EQ(LifecycleUnitState::PENDING_DISCARD, EXPECT_EQ(LifecycleUnitState::PENDING_DISCARD,
background_lifecycle_unit->GetState()); background_lifecycle_unit->GetState());
task_runner_->FastForwardBy(kProactiveDiscardFreezeTimeout); TransitionFromPendingDiscardToDiscardedIfNeeded(DiscardReason::kProactive,
background_lifecycle_unit);
EXPECT_EQ(LifecycleUnitState::DISCARDED, EXPECT_EQ(LifecycleUnitState::DISCARDED,
background_lifecycle_unit->GetState()); background_lifecycle_unit->GetState());
......
...@@ -44,6 +44,14 @@ content::Visibility TestLifecycleUnit::GetVisibility() const { ...@@ -44,6 +44,14 @@ content::Visibility TestLifecycleUnit::GetVisibility() const {
return content::Visibility::VISIBLE; return content::Visibility::VISIBLE;
} }
::mojom::LifecycleUnitLoadingState TestLifecycleUnit::GetLoadingState() const {
return ::mojom::LifecycleUnitLoadingState::LOADED;
}
bool TestLifecycleUnit::Load() {
return false;
}
bool TestLifecycleUnit::Freeze() { bool TestLifecycleUnit::Freeze() {
return false; return false;
} }
......
...@@ -31,6 +31,8 @@ class TestLifecycleUnit : public LifecycleUnitBase { ...@@ -31,6 +31,8 @@ class TestLifecycleUnit : public LifecycleUnitBase {
base::ProcessHandle GetProcessHandle() const override; base::ProcessHandle GetProcessHandle() const override;
SortKey GetSortKey() const override; SortKey GetSortKey() const override;
content::Visibility GetVisibility() const override; content::Visibility GetVisibility() const override;
LifecycleUnitLoadingState GetLoadingState() const override;
bool Load() override;
bool Freeze() override; bool Freeze() override;
int GetEstimatedMemoryFreedOnDiscardKB() const override; int GetEstimatedMemoryFreedOnDiscardKB() const override;
bool CanPurge() const override; bool CanPurge() const override;
......
...@@ -16,12 +16,33 @@ table th { ...@@ -16,12 +16,33 @@ table th {
table th { table th {
-webkit-padding-end: 16px; -webkit-padding-end: 16px;
background: rgb(224, 236, 255); background: rgb(224, 236, 255);
cursor: pointer;
padding-bottom: 4px; padding-bottom: 4px;
padding-top: 4px; padding-top: 4px;
white-space: nowrap; white-space: nowrap;
} }
table th[data-sort-key] {
cursor: pointer;
}
table th div.header-cell-container {
align-items: center;
display: flex;
justify-content: flex-start;
/* Make sure mouse events pass through to the underlying <th>. */
pointer-events: none;
}
table th div.header-cell-container div {
/* Make sure mouse events pass through to the underlying <th>. */
pointer-events: none;
}
table th div.header-cell-container div div {
/* Make sure mouse events pass through to the underlying <th>. */
pointer-events: none;
}
table td.title-cell { table td.title-cell {
max-width: 200px; max-width: 200px;
overflow: hidden; overflow: hidden;
...@@ -51,13 +72,28 @@ table td.tab-url-cell { ...@@ -51,13 +72,28 @@ table td.tab-url-cell {
white-space: nowrap; white-space: nowrap;
} }
table td.visibility-cell {
width: 6em;
}
table td.loading-state-cell {
width: 6em;
}
table td.state-cell {
width: 10em;
}
table td.boolean-cell, table td.boolean-cell,
table td.discard-count-cell { table td.discard-count-cell,
table td.reactivation-score-cell,
table td.site-engagement-score-cell,
table td.utility-rank-cell {
text-align: center; text-align: center;
} }
table td div.is-auto-discardable-link, table td div.is-auto-discardable-link,
table td.discard-links-cell { table td.actions-cell {
font-size: 0.6rem; font-size: 0.6rem;
} }
...@@ -65,12 +101,17 @@ table tr:hover { ...@@ -65,12 +101,17 @@ table tr:hover {
background: rgb(255, 255, 187); background: rgb(255, 255, 187);
} }
th.sort-column::after { th div.header-cell-container::after {
content: '▲';
opacity: 0;
}
th.sort-column div.header-cell-container::after {
content: '▲'; content: '▲';
position: absolute; opacity: 1;
} }
th[data-sort-reverse].sort-column::after { th[data-sort-reverse].sort-column div.header-cell-container::after {
content: '▼'; content: '▼';
position: absolute; opacity: 1;
} }
...@@ -32,16 +32,90 @@ general use and is not localized. ...@@ -32,16 +32,90 @@ general use and is not localized.
<table id="tab-discard-info-table"> <table id="tab-discard-info-table">
<thead> <thead>
<tr id="tab-discards-info-table-header"> <tr id="tab-discards-info-table-header">
<th data-sort-key="utilityRank" class="sort-column">Utility Rank</th> <th data-sort-key="utilityRank" class="sort-column">
<th data-sort-key="reactivationScore">Reactivation Score</th> <div class="header-cell-container">
<th data-sort-key="title">Tab Title</th> <div>
<th data-sort-key="tabUrl">Tab URL</th> <div>Utility</div>
<th data-sort-key="visibility">Visibility</th> <div>Rank<div>
<th data-sort-key="state">State</th> </div>
<th data-sort-key="isMedia">Media</th> </div>
<th data-sort-key="discardCount">Discard Count</th> </th>
<th data-sort-key="isAutoDiscardable">Auto Discardable</th> <th data-sort-key="reactivationScore">
<th data-sort-key="lastActiveSeconds">Last Active</th> <div class="header-cell-container">
<div>
<div>Reactivation</div>
<div>Score</div>
</div>
</div>
</th>
<th data-sort-key="siteEngagementScore">
<div class="header-cell-container">
<div>
<div>Site</div>
<div>Engagement</div>
<div>Score</div>
</div>
</div>
</th>
<th data-sort-key="title">
<div class="header-cell-container">
Tab Title
</div>
</th>
<th data-sort-key="tabUrl">
<div class="header-cell-container">
Tab URL
</div>
</th>
<th data-sort-key="visibility">
<div class="header-cell-container">
Visibility
</div>
</th>
<th data-sort-key="loadingState">
<div class="header-cell-container">
Loading State
</div>
</th>
<th data-sort-key="state">
<div class="header-cell-container">
<div>
<div>Lifecycle</div>
<div>State</div>
</div>
</div>
</th>
<th data-sort-key="isMedia">
<div class="header-cell-container">
Media
</div>
</th>
<th data-sort-key="discardCount">
<div class="header-cell-container">
<div>
<div>Discard</div>
<div>Count</div>
</div>
</div>
</div></th>
<th data-sort-key="isAutoDiscardable">
<div class="header-cell-container">
<div>
<div>Auto</div>
<div>Discardable</div>
</div>
</div>
</th>
<th data-sort-key="lastActiveSeconds">
<div class="header-cell-container">
Last Active
</div>
</th>
<th>
<div class="header-cell-container">
Actions
</div>
</th>
</tr> </tr>
</thead> </thead>
<tbody id="tab-discards-info-table-body"> <tbody id="tab-discards-info-table-body">
...@@ -51,6 +125,7 @@ general use and is not localized. ...@@ -51,6 +125,7 @@ general use and is not localized.
<tr> <tr>
<td class="utility-rank-cell"></td> <td class="utility-rank-cell"></td>
<td class="reactivation-score-cell"></td> <td class="reactivation-score-cell"></td>
<td class="site-engagement-score-cell"></td>
<td class="title-cell"> <td class="title-cell">
<div class="title-cell-container"> <div class="title-cell-container">
<div class="favicon-div"></div> <div class="favicon-div"></div>
...@@ -59,22 +134,24 @@ general use and is not localized. ...@@ -59,22 +134,24 @@ general use and is not localized.
</td> </td>
<td class="tab-url-cell"></td> <td class="tab-url-cell"></td>
<td class="visibility-cell"></td> <td class="visibility-cell"></td>
<td class="loading-state-cell"></td>
<td class="state-cell"></td> <td class="state-cell"></td>
<td class="is-media-cell boolean-cell"></td> <td class="is-media-cell boolean-cell"></td>
<td class="discard-count-cell"></td> <td class="discard-count-cell"></td>
<td class="is-auto-discardable-cell boolean-cell"> <td class="is-auto-discardable-cell boolean-cell">
<div class="is-auto-discardable-div"></div> <div class="is-auto-discardable-div"></div>
<div is="action-link" class="is-auto-discardable-link"> <div is="action-link" class="is-auto-discardable-link">
Toggle [Toggle]
</div> </div>
</td> </td>
<td class="last-active-cell"></td> <td class="last-active-cell"></td>
<td class="discard-links-cell"> <td class="actions-cell">
<div is="action-link" class="load-link">[Load]</div>
<div is="action-link" class="freeze-link">[Freeze]</div>
<div is="action-link" class="discard-link">[Discard]</div> <div is="action-link" class="discard-link">[Discard]</div>
<div is="action-link" class="discard-urgent-link"> <div is="action-link" class="discard-urgent-link">
[Urgent Discard] [Urgent Discard]
</div> </div>
<div is="action-link" class="freeze-link">[Freeze]</div>
</td> </td>
</tr> </tr>
</template> </template>
......
...@@ -71,14 +71,17 @@ cr.define('discards', function() { ...@@ -71,14 +71,17 @@ cr.define('discards', function() {
} }
// Compares numeric fields. // Compares numeric fields.
// Note: Visibility and state are represented as a numeric value. // NOTE: visibility, loadingState and state are represented as a numeric
// value.
if ([ if ([
'visibility', 'visibility',
'loadingState',
'state', 'state',
'discardCount', 'discardCount',
'utilityRank', 'utilityRank',
'reactivationScore', 'reactivationScore',
'lastActiveSeconds', 'lastActiveSeconds',
'siteEngagementScore',
].includes(sortKey)) { ].includes(sortKey)) {
return val1 - val2; return val1 - val2;
} }
...@@ -203,6 +206,24 @@ cr.define('discards', function() { ...@@ -203,6 +206,24 @@ cr.define('discards', function() {
assertNotReached('Unsupported visibility: ' + visibility); assertNotReached('Unsupported visibility: ' + visibility);
} }
/**
* Returns a string representation of a loading state enum value for display
* in a table.
* @param {int} loadingState A value in LifecycleUnitLoadingState enum.
* @return {string} A string representation of the loading state.
*/
function loadingStateToString(loadingState) {
switch (loadingState) {
case 0:
return 'unloaded';
case 1:
return 'loading';
case 2:
return 'loaded';
}
assertNotReached('Unsupport loadingState: ' + loadingState);
}
function lifecycleStateToString(state) { function lifecycleStateToString(state) {
switch (state) { switch (state) {
case mojom.LifecycleUnitState.ACTIVE: case mojom.LifecycleUnitState.ACTIVE:
...@@ -253,6 +274,26 @@ cr.define('discards', function() { ...@@ -253,6 +274,26 @@ cr.define('discards', function() {
.then(stableUpdateTabDiscardsInfoTable()); .then(stableUpdateTabDiscardsInfoTable());
}); });
// Set up the listeners for load links.
let loadListener = function(e) {
// Get the info backing this row.
let info = infos[getRowIndex(e.target)];
// Perform the action.
uiHandler.loadById(info.id);
};
let loadLink = row.querySelector('.load-link');
loadLink.addEventListener('click', loadListener);
// Set up the listeners for freeze links.
let freezeListener = function(e) {
// Get the info backing this row.
let info = infos[getRowIndex(e.target)];
// Perform the action.
uiHandler.freezeById(info.id);
};
let freezeLink = row.querySelector('.freeze-link');
freezeLink.addEventListener('click', freezeListener);
// Set up the listeners for discard links. // Set up the listeners for discard links.
let discardListener = function(e) { let discardListener = function(e) {
// Get the info backing this row. // Get the info backing this row.
...@@ -272,21 +313,19 @@ cr.define('discards', function() { ...@@ -272,21 +313,19 @@ cr.define('discards', function() {
discardLink.addEventListener('click', discardListener); discardLink.addEventListener('click', discardListener);
discardUrgentLink.addEventListener('click', discardListener); discardUrgentLink.addEventListener('click', discardListener);
// Set up the listeners for freeze links.
let lifecycleListener = function(e) {
// Get the info backing this row.
let info = infos[getRowIndex(e.target)];
// Perform the action.
uiHandler.freezeById(info.id);
};
let freezeLink = row.querySelector('.freeze-link');
freezeLink.addEventListener('click', lifecycleListener);
return row; return row;
} }
/**
* Given an "action-link" element, enables or disables it.
*/
function setActionLinkEnabled(element, enabled) {
if (enabled)
element.removeAttribute('disabled');
else
element.setAttribute('disabled', '');
}
/** /**
* Updates a tab discards info table row in place. Sets/unsets 'disabled' * Updates a tab discards info table row in place. Sets/unsets 'disabled'
* attributes on action-links as necessary, and populates all contents. * attributes on action-links as necessary, and populates all contents.
...@@ -297,16 +336,23 @@ cr.define('discards', function() { ...@@ -297,16 +336,23 @@ cr.define('discards', function() {
info.utilityRank.toString(); info.utilityRank.toString();
row.querySelector('.reactivation-score-cell').textContent = row.querySelector('.reactivation-score-cell').textContent =
info.hasReactivationScore ? info.reactivationScore.toFixed(4) : 'N/A'; info.hasReactivationScore ? info.reactivationScore.toFixed(4) : 'N/A';
row.querySelector('.site-engagement-score-cell').textContent =
info.siteEngagementScore.toFixed(1);
row.querySelector('.favicon-div').style.backgroundImage = row.querySelector('.favicon-div').style.backgroundImage =
cr.icon.getFavicon(info.tabUrl); cr.icon.getFavicon(info.tabUrl);
row.querySelector('.title-div').textContent = info.title; row.querySelector('.title-div').textContent = info.title;
row.querySelector('.tab-url-cell').textContent = info.tabUrl; row.querySelector('.tab-url-cell').textContent = info.tabUrl;
row.querySelector('.visibility-cell').textContent = row.querySelector('.visibility-cell').textContent =
visibilityToString(info.visibility); visibilityToString(info.visibility);
row.querySelector('.loading-state-cell').textContent =
loadingStateToString(info.loadingState);
row.querySelector('.is-media-cell').textContent = row.querySelector('.is-media-cell').textContent =
boolToString(info.isMedia); boolToString(info.isMedia);
// The lifecycle state is meaningless for 'unloaded' tabs.
row.querySelector('.state-cell').textContent = row.querySelector('.state-cell').textContent =
lifecycleStateToString(info.state); (info.loadingState != mojom.LifecycleUnitLoadingState.UNLOADED) ?
lifecycleStateToString(info.state) :
'';
row.querySelector('.discard-count-cell').textContent = row.querySelector('.discard-count-cell').textContent =
info.discardCount.toString(); info.discardCount.toString();
row.querySelector('.is-auto-discardable-div').textContent = row.querySelector('.is-auto-discardable-div').textContent =
...@@ -314,31 +360,45 @@ cr.define('discards', function() { ...@@ -314,31 +360,45 @@ cr.define('discards', function() {
row.querySelector('.last-active-cell').textContent = row.querySelector('.last-active-cell').textContent =
lastActiveToString(info.lastActiveSeconds); lastActiveToString(info.lastActiveSeconds);
// Enable/disable action links as appropriate.
row.querySelector('.is-auto-discardable-link').removeAttribute('disabled'); row.querySelector('.is-auto-discardable-link').removeAttribute('disabled');
let loadLink = row.querySelector('.load-link');
let freezeLink = row.querySelector('.freeze-link');
let discardLink = row.querySelector('.discard-link'); let discardLink = row.querySelector('.discard-link');
let discardUrgentLink = row.querySelector('.discard-urgent-link'); let discardUrgentLink = row.querySelector('.discard-urgent-link');
let freezeLink = row.querySelector('.freeze-link');
switch (info.state) { // Determine which action links should be enabled/disabled.
case mojom.LifecycleUnitState.ACTIVE: let loadEnabled = false;
discardLink.removeAttribute('disabled'); let freezeEnabled = false;
discardUrgentLink.removeAttribute('disabled'); let discardEnabled = false;
freezeLink.removeAttribute('disabled'); let discardUrgentEnabled = false;
break; if (info.loadingState == mojom.LifecycleUnitLoadingState.UNLOADED) {
case mojom.LifecycleUnitState.THROTTLED: loadEnabled = true;
case mojom.LifecycleUnitState.PENDING_FREEZE: } else {
case mojom.LifecycleUnitState.FROZEN: freezeEnabled = true;
discardLink.removeAttribute('disabled'); discardEnabled = true;
discardUrgentLink.removeAttribute('disabled'); discardUrgentEnabled = true;
freezeLink.setAttribute('disabled', ''); switch (info.state) {
break; case mojom.LifecycleUnitState.DISCARDED:
case mojom.LifecycleUnitState.PENDING_DISCARD: case mojom.LifecycleUnitState.PENDING_DISCARD:
case mojom.LifecycleUnitState.DISCARDED: discardUrgentEnabled = false;
discardLink.setAttribute('disabled', ''); discardEnabled = false;
discardUrgentLink.setAttribute('disabled', ''); // Deliberately fall through.
freezeLink.setAttribute('disabled', '');
break; case mojom.LifecycleUnitState.FROZEN:
case mojom.LifecycleUnitState.PENDING_FREEZE:
freezeEnabled = false;
// Deliberately fall through.
case mojom.LifecycleUnitState.THROTTLED:
case mojom.LifecycleUnitState.ACTIVE:
// Everything stays enabled,
}
} }
setActionLinkEnabled(loadLink, loadEnabled);
setActionLinkEnabled(freezeLink, freezeEnabled);
setActionLinkEnabled(discardLink, discardEnabled);
setActionLinkEnabled(discardUrgentLink, discardUrgentEnabled);
} }
/** /**
......
...@@ -22,6 +22,8 @@ struct TabDiscardsInfo { ...@@ -22,6 +22,8 @@ struct TabDiscardsInfo {
string title; string title;
// The visibility of the LifecycleUnit. // The visibility of the LifecycleUnit.
LifecycleUnitVisibility visibility; LifecycleUnitVisibility visibility;
// The loading state of the LifecycleUnit.
LifecycleUnitLoadingState loading_state;
// The state of the LifecycleUnit. // The state of the LifecycleUnit.
LifecycleUnitState state; LifecycleUnitState state;
// If the tab is currently using media functionality (casting, WebRTC, playing // If the tab is currently using media functionality (casting, WebRTC, playing
...@@ -47,6 +49,8 @@ struct TabDiscardsInfo { ...@@ -47,6 +49,8 @@ struct TabDiscardsInfo {
bool has_reactivation_score; bool has_reactivation_score;
// Tab Ranker reactivation score, if |has_reactivation_score| is true. // Tab Ranker reactivation score, if |has_reactivation_score| is true.
double reactivation_score; double reactivation_score;
// Site engagement score.
double site_engagement_score;
}; };
// Interface for providing information about discards. Lives in the browser // Interface for providing information about discards. Lives in the browser
...@@ -70,6 +74,9 @@ interface DiscardsDetailsProvider { ...@@ -70,6 +74,9 @@ interface DiscardsDetailsProvider {
// Freezes a tab given its |tab_id|. // Freezes a tab given its |tab_id|.
FreezeById(int32 tab_id); FreezeById(int32 tab_id);
// Loads a tab given its |tab_id|.
LoadById(int32 tab_id);
// Discards the least important tab. If |urgent| is specified the unload // Discards the least important tab. If |urgent| is specified the unload
// handlers will not be run, and the tab will be unloaded with prejudice. // handlers will not be run, and the tab will be unloaded with prejudice.
// This can fail to discard a tab if no tabs are currently considered // This can fail to discard a tab if no tabs are currently considered
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/engagement/site_engagement_service.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/resource_coordinator/discard_reason.h" #include "chrome/browser/resource_coordinator/discard_reason.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit.h" #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
...@@ -22,6 +23,8 @@ ...@@ -22,6 +23,8 @@
#include "chrome/browser/ui/webui/discards/discards.mojom.h" #include "chrome/browser/ui/webui/discards/discards.mojom.h"
#include "chrome/common/webui_url_constants.h" #include "chrome/common/webui_url_constants.h"
#include "chrome/grit/browser_resources.h" #include "chrome/grit/browser_resources.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h" #include "content/public/browser/web_ui_data_source.h"
...@@ -61,6 +64,21 @@ resource_coordinator::LifecycleUnit* GetLifecycleUnitById(int32_t id) { ...@@ -61,6 +64,21 @@ resource_coordinator::LifecycleUnit* GetLifecycleUnitById(int32_t id) {
return nullptr; return nullptr;
} }
double GetSiteEngagementScore(content::WebContents* contents) {
// Get the active navigation entry. Restored tabs should always have one.
auto& controller = contents->GetController();
auto* nav_entry =
controller.GetEntryAtIndex(controller.GetCurrentEntryIndex());
DCHECK(nav_entry);
auto* engagement_svc = SiteEngagementService::Get(
Profile::FromBrowserContext(contents->GetBrowserContext()));
double engagement =
engagement_svc->GetDetails(nav_entry->GetURL()).total_score;
return engagement;
}
class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider { class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider {
public: public:
// This instance is deleted when the supplied pipe is destroyed. // This instance is deleted when the supplied pipe is destroyed.
...@@ -97,6 +115,7 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider { ...@@ -97,6 +115,7 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider {
info->title = base::UTF16ToUTF8(lifecycle_unit->GetTitle()); info->title = base::UTF16ToUTF8(lifecycle_unit->GetTitle());
info->visibility = info->visibility =
GetLifecycleUnitVisibility(lifecycle_unit->GetVisibility()); GetLifecycleUnitVisibility(lifecycle_unit->GetVisibility());
info->loading_state = lifecycle_unit->GetLoadingState();
info->state = lifecycle_unit->GetState(); info->state = lifecycle_unit->GetState();
info->is_media = tab_lifecycle_unit_external->IsMediaTab(); info->is_media = tab_lifecycle_unit_external->IsMediaTab();
info->discard_count = tab_lifecycle_unit_external->GetDiscardCount(); info->discard_count = tab_lifecycle_unit_external->GetDiscardCount();
...@@ -117,6 +136,7 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider { ...@@ -117,6 +136,7 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider {
info->has_reactivation_score = reactivation_score.has_value(); info->has_reactivation_score = reactivation_score.has_value();
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);
infos.push_back(std::move(info)); infos.push_back(std::move(info));
} }
...@@ -152,6 +172,12 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider { ...@@ -152,6 +172,12 @@ class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider {
lifecycle_unit->Freeze(); lifecycle_unit->Freeze();
} }
void LoadById(int32_t id) override {
auto* lifecycle_unit = GetLifecycleUnitById(id);
if (lifecycle_unit)
lifecycle_unit->Load();
}
void Discard(bool urgent, DiscardCallback callback) override { void Discard(bool urgent, DiscardCallback callback) override {
resource_coordinator::TabManager* tab_manager = resource_coordinator::TabManager* tab_manager =
g_browser_process->GetTabManager(); g_browser_process->GetTabManager();
......
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