Skip to content

Commit ce49bf6

Browse files
committed
Fix some tab lifetime management issues in TabLifecycleUnitSource.
Move the WebContentsObserver from TabLifeCycleUnit to TabLifeCycleUnitSource, this allows for a better tracking of the WebContents lifetime, in some situation a WebContents might get detached from the TabStrip and then destroyed, which mean that we won't get a TabClosingAt notification for this tab destruction. Another solution would be to implement the TabStripModelObserver::TabDetachedAt function and track the tabs which are in a detached state but this is slightly more complex because TabDetachedAt might be called for several reasons: - A TabDetachedAt usually come after a TabClosedAt event. - TabDetachedAt might be followed by TabInsertedAt, or not if it get destroyed. because of this we won't know if we should keep the TabLifeCycleUnit for this WebContents around (i.e. if it'll get re-inserted in a tab strip) or destroy it because it's being destroyed. Observing WebContentsObserver::WebContentsDestroyed and moving the logic that was in TabClosingAt to this method address these issues, it's the same approach than the one we took in TabStatsTracker. Bug: 819352, 818454 Change-Id: Ibd3fe49b2798ade19ee781cb70eb30e52372d686 Reviewed-on: https://chromium-review.googlesource.com/952405 Commit-Queue: Sébastien Marchand <[email protected]> Reviewed-by: Chris Hamilton <[email protected]> Cr-Original-Commit-Position: refs/heads/master@{#541540}(cherry picked from commit 70546dc) Reviewed-on: https://chromium-review.googlesource.com/953367 Reviewed-by: Sébastien Marchand <[email protected]> Cr-Commit-Position: refs/branch-heads/3359@{#74} Cr-Branched-From: 66afc5e-refs/heads/master@{#540276}
1 parent 74361db commit ce49bf6

6 files changed

+99
-36
lines changed

chrome/browser/resource_coordinator/tab_lifecycle_unit.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ TabLifecycleUnitSource::TabLifecycleUnit::TabLifecycleUnit(
2727
base::ObserverList<TabLifecycleObserver>* observers,
2828
content::WebContents* web_contents,
2929
TabStripModel* tab_strip_model)
30-
: content::WebContentsObserver(web_contents),
31-
observers_(observers),
30+
: observers_(observers),
31+
web_contents_(web_contents),
3232
tab_strip_model_(tab_strip_model) {
3333
DCHECK(observers_);
34-
DCHECK(GetWebContents());
34+
DCHECK(web_contents_);
3535
DCHECK(tab_strip_model_);
3636
}
3737

@@ -48,7 +48,7 @@ void TabLifecycleUnitSource::TabLifecycleUnit::SetTabStripModel(
4848
void TabLifecycleUnitSource::TabLifecycleUnit::SetWebContents(
4949
content::WebContents* web_contents) {
5050
DCHECK(web_contents);
51-
Observe(web_contents);
51+
web_contents_ = web_contents;
5252
}
5353

5454
void TabLifecycleUnitSource::TabLifecycleUnit::SetFocused(bool focused) {
@@ -264,7 +264,7 @@ bool TabLifecycleUnitSource::TabLifecycleUnit::Discard(
264264

265265
content::WebContents* TabLifecycleUnitSource::TabLifecycleUnit::GetWebContents()
266266
const {
267-
return web_contents();
267+
return web_contents_;
268268
}
269269

270270
bool TabLifecycleUnitSource::TabLifecycleUnit::IsMediaTab() const {
@@ -324,7 +324,7 @@ TabLifecycleUnitSource::TabLifecycleUnit::GetRenderProcessHost() const {
324324
return GetWebContents()->GetMainFrame()->GetProcess();
325325
}
326326

327-
void TabLifecycleUnitSource::TabLifecycleUnit::DidStartLoading() {
327+
void TabLifecycleUnitSource::TabLifecycleUnit::OnDidStartLoading() {
328328
if (GetState() == State::DISCARDED) {
329329
SetState(State::LOADED);
330330
OnDiscardedStateChange();

chrome/browser/resource_coordinator/tab_lifecycle_unit.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
1313
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
1414
#include "chrome/browser/resource_coordinator/time.h"
15-
#include "content/public/browser/web_contents_observer.h"
1615

1716
class TabStripModel;
1817

@@ -36,8 +35,7 @@ static constexpr base::TimeDelta kTabFocusedProtectionTime =
3635
// Represents a tab.
3736
class TabLifecycleUnitSource::TabLifecycleUnit
3837
: public LifecycleUnitBase,
39-
public TabLifecycleUnitExternal,
40-
public content::WebContentsObserver {
38+
public TabLifecycleUnitExternal {
4139
public:
4240
// |observers| is a list of observers to notify when the discarded state or
4341
// the auto-discardable state of this tab changes. It can be modified outside
@@ -69,6 +67,10 @@ class TabLifecycleUnitSource::TabLifecycleUnit
6967
// "recently audible" state of the tab changes.
7068
void SetRecentlyAudible(bool recently_audible);
7169

70+
// Invoked when we receive a DidStartLoading notification for the WebContents
71+
// used by this tab.
72+
void OnDidStartLoading();
73+
7274
// LifecycleUnit:
7375
TabLifecycleUnitExternal* AsTabLifecycleUnitExternal() override;
7476
base::string16 GetTitle() const override;
@@ -96,13 +98,13 @@ class TabLifecycleUnitSource::TabLifecycleUnit
9698
// Returns the RenderProcessHost associated with this tab.
9799
content::RenderProcessHost* GetRenderProcessHost() const;
98100

99-
// content::WebContentsObserver:
100-
void DidStartLoading() override;
101-
102101
// List of observers to notify when the discarded state or the auto-
103102
// discardable state of this tab changes.
104103
base::ObserverList<TabLifecycleObserver>* observers_;
105104

105+
// The WebContents for this tab.
106+
content::WebContents* web_contents_;
107+
106108
// TabStripModel to which this tab belongs.
107109
TabStripModel* tab_strip_model_;
108110

chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "chrome/browser/ui/browser_list.h"
1515
#include "chrome/browser/ui/browser_window.h"
1616
#include "chrome/browser/ui/tabs/tab_strip_model.h"
17+
#include "content/public/browser/web_contents_observer.h"
1718

1819
namespace resource_coordinator {
1920

@@ -63,6 +64,41 @@ void TabLifecycleUnitSource::SetFocusedTabStripModelForTesting(
6364
UpdateFocusedTab();
6465
}
6566

67+
// A specialization of content::WebContentsObserver that allows to properly
68+
// track the destruction of tabs.
69+
class TabLifecycleUnitSource::TabLifecycleUnitWebContentsObserver
70+
: public content::WebContentsObserver {
71+
public:
72+
TabLifecycleUnitWebContentsObserver(
73+
content::WebContents* web_contents,
74+
TabLifecycleUnit* lifecycle_unit,
75+
TabLifecycleUnitSource* lifecycle_unit_source)
76+
: content::WebContentsObserver(web_contents),
77+
lifecycle_unit_(lifecycle_unit),
78+
lifecycle_unit_source_(lifecycle_unit_source) {}
79+
80+
void DidStartLoading() override {
81+
DCHECK(lifecycle_unit_);
82+
lifecycle_unit_->OnDidStartLoading();
83+
}
84+
85+
void WebContentsDestroyed() override {
86+
lifecycle_unit_source_->OnWebContentsDestroyed(web_contents());
87+
// The call above will free |this| and so nothing should be done on this
88+
// object starting from here.
89+
}
90+
91+
private:
92+
// The lifecycle unit for this tab.
93+
TabLifecycleUnit* lifecycle_unit_;
94+
95+
// The lifecycle unit source that should get notified when the observed
96+
// WebContents is about to be destroyed.
97+
TabLifecycleUnitSource* lifecycle_unit_source_;
98+
99+
DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitWebContentsObserver);
100+
};
101+
66102
TabStripModel* TabLifecycleUnitSource::GetFocusedTabStripModel() const {
67103
if (focused_tab_strip_model_for_testing_)
68104
return focused_tab_strip_model_for_testing_;
@@ -113,6 +149,11 @@ void TabLifecycleUnitSource::TabInsertedAt(TabStripModel* tab_strip_model,
113149
// Add a self-owned observer to the LifecycleUnit to record metrics.
114150
lifecycle_unit->AddObserver(new DiscardMetricsLifecycleUnitObserver());
115151

152+
// Track the WebContents used by this tab.
153+
web_contents_observers_.insert(std::make_pair(
154+
contents, std::make_unique<TabLifecycleUnitWebContentsObserver>(
155+
contents, lifecycle_unit, this)));
156+
116157
NotifyLifecycleUnitCreated(lifecycle_unit);
117158
} else {
118159
// A tab was moved to another window.
@@ -122,18 +163,6 @@ void TabLifecycleUnitSource::TabInsertedAt(TabStripModel* tab_strip_model,
122163
}
123164
}
124165

125-
void TabLifecycleUnitSource::TabClosingAt(TabStripModel* tab_strip_model,
126-
content::WebContents* contents,
127-
int index) {
128-
auto it = tabs_.find(contents);
129-
DCHECK(it != tabs_.end());
130-
TabLifecycleUnit* lifecycle_unit = it->second.get();
131-
if (focused_tab_lifecycle_unit_ == lifecycle_unit)
132-
focused_tab_lifecycle_unit_ = nullptr;
133-
NotifyLifecycleUnitDestroyed(lifecycle_unit);
134-
tabs_.erase(contents);
135-
}
136-
137166
void TabLifecycleUnitSource::ActiveTabChanged(
138167
content::WebContents* old_contents,
139168
content::WebContents* new_contents,
@@ -151,7 +180,11 @@ void TabLifecycleUnitSource::TabReplacedAt(TabStripModel* tab_strip_model,
151180
DCHECK(it != tabs_.end());
152181
std::unique_ptr<TabLifecycleUnit> lifecycle_unit = std::move(it->second);
153182
lifecycle_unit->SetWebContents(new_contents);
183+
web_contents_observers_.erase(old_contents);
154184
tabs_.erase(it);
185+
web_contents_observers_.insert(std::make_pair(
186+
new_contents, std::make_unique<TabLifecycleUnitWebContentsObserver>(
187+
new_contents, lifecycle_unit.get(), this)));
155188
tabs_[new_contents] = std::move(lifecycle_unit);
156189
}
157190

@@ -178,4 +211,15 @@ void TabLifecycleUnitSource::OnBrowserNoLongerActive(Browser* browser) {
178211
UpdateFocusedTab();
179212
}
180213

214+
void TabLifecycleUnitSource::OnWebContentsDestroyed(
215+
content::WebContents* contents) {
216+
auto it = tabs_.find(contents);
217+
DCHECK(it != tabs_.end());
218+
TabLifecycleUnit* lifecycle_unit = it->second.get();
219+
if (focused_tab_lifecycle_unit_ == lifecycle_unit)
220+
focused_tab_lifecycle_unit_ = nullptr;
221+
NotifyLifecycleUnitDestroyed(lifecycle_unit);
222+
tabs_.erase(contents);
223+
}
224+
181225
} // namespace resource_coordinator

chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class TabLifecycleUnitSource : public BrowserListObserver,
5353
friend class TabLifecycleUnitTest;
5454

5555
class TabLifecycleUnit;
56+
class TabLifecycleUnitWebContentsObserver;
5657

5758
// Returns the TabStripModel of the focused browser window, if any.
5859
TabStripModel* GetFocusedTabStripModel() const;
@@ -71,9 +72,6 @@ class TabLifecycleUnitSource : public BrowserListObserver,
7172
content::WebContents* contents,
7273
int index,
7374
bool foreground) override;
74-
void TabClosingAt(TabStripModel* tab_strip_model,
75-
content::WebContents* contents,
76-
int index) override;
7775
void ActiveTabChanged(content::WebContents* old_contents,
7876
content::WebContents* new_contents,
7977
int index,
@@ -90,6 +88,8 @@ class TabLifecycleUnitSource : public BrowserListObserver,
9088
void OnBrowserSetLastActive(Browser* browser) override;
9189
void OnBrowserNoLongerActive(Browser* browser) override;
9290

91+
void OnWebContentsDestroyed(content::WebContents* contents);
92+
9393
// Tracks the BrowserList and all TabStripModels.
9494
BrowserTabStripTracker browser_tab_strip_tracker_;
9595

@@ -108,6 +108,12 @@ class TabLifecycleUnitSource : public BrowserListObserver,
108108
// changes.
109109
base::ObserverList<TabLifecycleObserver> tab_lifecycle_observers_;
110110

111+
// Map from content::WebContents to TabLifecycleUnitWebContentsObserver,
112+
// used to track the WebContents destruction.
113+
base::flat_map<content::WebContents*,
114+
std::unique_ptr<TabLifecycleUnitWebContentsObserver>>
115+
web_contents_observers_;
116+
111117
DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSource);
112118
};
113119

chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ bool IsFocused(LifecycleUnit* lifecycle_unit) {
7373
class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness {
7474
protected:
7575
TabLifecycleUnitSourceTest()
76-
: scoped_set_tick_clock_for_testing_(&test_clock_) {
77-
}
76+
: scoped_set_tick_clock_for_testing_(&test_clock_) {}
7877

7978
void SetUp() override {
8079
ChromeRenderViewHostTestHarness::SetUp();
@@ -443,4 +442,22 @@ TEST_F(TabLifecycleUnitSourceTest, CanOnlyDiscardOnce) {
443442
#endif
444443
}
445444

445+
TEST_F(TabLifecycleUnitSourceTest, HandleWebContentsDestruction) {
446+
LifecycleUnit* first_lifecycle_unit = nullptr;
447+
LifecycleUnit* second_lifecycle_unit = nullptr;
448+
CreateTwoTabs(true /* focus_tab_strip */, &first_lifecycle_unit,
449+
&second_lifecycle_unit);
450+
451+
// Detach the second WebContents and manually destroy it, the
452+
// observer should be notified.
453+
EXPECT_CALL(source_observer_,
454+
OnLifecycleUnitDestroyed(second_lifecycle_unit));
455+
delete tab_strip_model_->DetachWebContentsAt(1);
456+
457+
testing::Mock::VerifyAndClear(&source_observer_);
458+
459+
EXPECT_CALL(source_observer_, OnLifecycleUnitDestroyed(first_lifecycle_unit));
460+
tab_strip_model_->CloseAllTabs();
461+
}
462+
446463
} // namespace resource_coordinator

chrome/browser/resource_coordinator/tab_manager.cc

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -482,13 +482,7 @@ void TabManager::PurgeBackgroundedTabsIfNeeded() {
482482
DCHECK(tab_lifecycle_unit_external);
483483
content::WebContents* content =
484484
tab_lifecycle_unit_external->GetWebContents();
485-
// TODO(fdoray): Check if TabLifecycleUnitSource should override
486-
// WebContentsObserver::WebContentsDestroyed() as in some situations a
487-
// WebContents might get destroyed without a call to
488-
// TabStripModelObserver::TabClosingAt, in this case we'll have a
489-
// TabLifecycleUnitExternal that points to a null WebContents.
490-
if (content == nullptr)
491-
return;
485+
DCHECK(content);
492486

493487
content::RenderProcessHost* render_process_host =
494488
content->GetMainFrame()->GetProcess();

0 commit comments

Comments
 (0)