Skip to content

Commit a88bdde

Browse files
committed
Fixed crash when toggling playlist panel while it's playing
fix brave/brave-browser#45144
1 parent 1e176b4 commit a88bdde

File tree

5 files changed

+83
-11
lines changed

5 files changed

+83
-11
lines changed

browser/playlist/test/playlist_browsertest.cc

+42
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "base/run_loop.h"
1212
#include "base/test/bind.h"
1313
#include "base/test/mock_callback.h"
14+
#include "base/test/run_until.h"
1415
#include "base/test/scoped_feature_list.h"
1516
#include "brave/browser/playlist/playlist_service_factory.h"
1617
#include "brave/browser/ui/brave_browser.h"
@@ -32,7 +33,10 @@
3233
#include "chrome/browser/profiles/profile.h"
3334
#include "chrome/browser/ui/browser.h"
3435
#include "chrome/browser/ui/browser_command_controller.h"
36+
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
3537
#include "chrome/browser/ui/views/frame/browser_view.h"
38+
#include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h"
39+
#include "chrome/browser/ui/views/side_panel/side_panel_ui.h"
3640
#include "chrome/browser/ui/views/tabs/tab.h"
3741
#include "chrome/test/base/platform_browser_test.h"
3842
#include "content/public/test/browser_test.h"
@@ -168,6 +172,44 @@ class PlaylistBrowserTest : public PlatformBrowserTest {
168172
content::ContentMockCertVerifier mock_cert_verifier_;
169173
};
170174

175+
// Check toggling playlist panel while playing doesn't make crash.
176+
IN_PROC_BROWSER_TEST_F(PlaylistBrowserTest, PanelToggleTestWhilePlaying) {
177+
auto* panel_ui = browser()->GetFeatures().side_panel_ui();
178+
179+
// Open playlist panel.
180+
panel_ui->Show(SidePanelEntryId::kPlaylist);
181+
WaitUntil(base::BindLambdaForTesting(
182+
[&]() { return panel_ui->IsSidePanelShowing(); }));
183+
ASSERT_TRUE(
184+
base::test::RunUntil([&]() { return panel_ui->IsSidePanelShowing(); }));
185+
186+
auto* coordinator = PlaylistSidePanelCoordinator::FromBrowser(browser());
187+
ASSERT_TRUE(coordinator);
188+
coordinator->is_audible_for_testing_ = true;
189+
190+
// Close playlist panel check cached instances are still live.
191+
panel_ui->Close();
192+
EXPECT_TRUE(coordinator->contents_wrapper_);
193+
EXPECT_TRUE(coordinator->side_panel_web_view_);
194+
ASSERT_TRUE(
195+
base::test::RunUntil([&]() { return !panel_ui->IsSidePanelShowing(); }));
196+
197+
// Re-open playlist panel.
198+
panel_ui->Show(SidePanelEntryId::kPlaylist);
199+
ASSERT_TRUE(
200+
base::test::RunUntil([&]() { return panel_ui->IsSidePanelShowing(); }));
201+
202+
// Not audible. Check cached webview/contents are destroyed.
203+
coordinator->is_audible_for_testing_ = false;
204+
205+
// Close playlist panel. Check cached instances are all freed.
206+
panel_ui->Close();
207+
EXPECT_FALSE(coordinator->contents_wrapper_);
208+
EXPECT_FALSE(coordinator->side_panel_web_view_);
209+
ASSERT_TRUE(
210+
base::test::RunUntil([&]() { return !panel_ui->IsSidePanelShowing(); }));
211+
}
212+
171213
IN_PROC_BROWSER_TEST_F(PlaylistBrowserTest, AddItemsToList) {
172214
ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(),
173215
GetURL("/playlist/site_with_video.html")));

browser/ui/views/side_panel/playlist/playlist_side_panel_coordinator.cc

+24-8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "chrome/browser/ui/views/side_panel/side_panel_util.h"
2323
#include "chrome/grit/generated_resources.h"
2424
#include "components/grit/brave_components_strings.h"
25+
#include "content/public/browser/visibility.h"
26+
#include "content/public/browser/web_contents.h"
2527
#include "ui/base/l10n/l10n_util.h"
2628
#include "ui/base/models/image_model.h"
2729
#include "ui/views/vector_icons.h"
@@ -88,22 +90,34 @@ std::unique_ptr<views::View> PlaylistSidePanelCoordinator::CreateWebView(
8890

8991
Proxy::CreateForWebContents(contents_wrapper_->web_contents(),
9092
weak_ptr_factory_.GetWeakPtr());
91-
}
9293

93-
auto web_view = std::make_unique<PlaylistSidePanelWebView>(
94-
&GetBrowser(), scope, base::DoNothing(), contents_wrapper_.get());
95-
side_panel_web_view_ = web_view->GetWeakPtr();
94+
// |web_view_| is also created and cached together with |contents_wrapper_|.
95+
// Why caching webview also? When SidePanelWebView is created, its contents
96+
// is marked as side panel to make its loading faster at
97+
// SidePanelLoadingVoter::MarkAsSidePanel(). However, upstream has
98+
// assumption that it should have initial state(not url loaded before)
99+
// except it's preloaded side panel. If we create PlaylistSidePanelWebView
100+
// whenever opening, we conflict with it as playlist could still play even
101+
// it's closed. By caching webview also, we could avoid that conflict.
102+
CHECK(!side_panel_web_view_);
103+
side_panel_web_view_ = std::make_unique<PlaylistSidePanelWebView>(
104+
&GetBrowser(), scope, base::DoNothing(), contents_wrapper_.get());
105+
side_panel_web_view_->set_owned_by_client();
106+
}
96107

97108
if (!should_create_contents_wrapper) {
98109
// SidePanelWebView's initial visibility is hidden. Thus, we need to
99110
// call this manually when we don't reload the web contents.
100111
// Calling this will also mark that the web contents is ready to go.
101-
web_view->ShowUI();
112+
side_panel_web_view_->ShowUI();
102113
}
103114

104-
view_observation_.Observe(web_view.get());
115+
auto view = std::make_unique<views::View>();
116+
view->SetUseDefaultFillLayout(true);
117+
view->AddChildViewRaw(side_panel_web_view_.get());
118+
view_observation_.Observe(view.get());
105119

106-
return web_view;
120+
return view;
107121
}
108122

109123
BrowserView* PlaylistSidePanelCoordinator::GetBrowserView() {
@@ -118,8 +132,10 @@ void PlaylistSidePanelCoordinator::OnViewIsDeleting(views::View* view) {
118132

119133
void PlaylistSidePanelCoordinator::DestroyWebContentsIfNeeded() {
120134
DCHECK(contents_wrapper_);
121-
if (!contents_wrapper_->web_contents()->IsCurrentlyAudible()) {
135+
if (!is_audible_for_testing_ &&
136+
!contents_wrapper_->web_contents()->IsCurrentlyAudible()) {
122137
contents_wrapper_.reset();
138+
side_panel_web_view_.reset();
123139
}
124140
}
125141

browser/ui/views/side_panel/playlist/playlist_side_panel_coordinator.h

+9-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <memory>
1010
#include <string>
1111

12+
#include "base/gtest_prod_util.h"
1213
#include "base/scoped_observation.h"
1314
#include "brave/browser/ui/views/side_panel/playlist/playlist_contents_wrapper.h"
1415
#include "brave/browser/ui/views/side_panel/playlist/playlist_side_panel_web_view.h"
@@ -18,6 +19,7 @@
1819

1920
namespace playlist {
2021
class PlaylistUI;
22+
FORWARD_DECLARE_TEST(PlaylistBrowserTest, PanelToggleTestWhilePlaying);
2123
} // namespace playlist
2224

2325
class Browser;
@@ -61,7 +63,7 @@ class PlaylistSidePanelCoordinator
6163
void LoadPlaylist(const std::string& playlist_id, const std::string& item_id);
6264

6365
base::WeakPtr<PlaylistSidePanelWebView> side_panel_web_view() {
64-
return side_panel_web_view_;
66+
return side_panel_web_view_ ? side_panel_web_view_->GetWeakPtr() : nullptr;
6567
}
6668

6769
BrowserView* GetBrowserView();
@@ -71,16 +73,20 @@ class PlaylistSidePanelCoordinator
7173

7274
private:
7375
friend class BrowserUserData<PlaylistSidePanelCoordinator>;
76+
FRIEND_TEST_ALL_PREFIXES(playlist::PlaylistBrowserTest,
77+
PanelToggleTestWhilePlaying);
7478

7579
void DestroyWebContentsIfNeeded();
7680

7781
std::unique_ptr<views::View> CreateWebView(SidePanelEntryScope& scope);
7882

83+
bool is_audible_for_testing_ = false;
7984
raw_ptr<Browser> browser_ = nullptr;
8085

86+
// Both are cached while it's playing even if playlist panel is closed.
87+
// Destroyed when panel is closed and not played.
8188
std::unique_ptr<PlaylistContentsWrapper> contents_wrapper_;
82-
83-
base::WeakPtr<PlaylistSidePanelWebView> side_panel_web_view_;
89+
std::unique_ptr<PlaylistSidePanelWebView> side_panel_web_view_;
8490

8591
base::ScopedObservation<views::View, views::ViewObserver> view_observation_{
8692
this};

browser/ui/views/side_panel/playlist/playlist_side_panel_web_view.cc

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include "brave/browser/ui/views/side_panel/playlist/playlist_side_panel_web_view.h"
77

8+
#include "ui/base/metadata/metadata_impl_macros.h"
9+
810
PlaylistSidePanelWebView::PlaylistSidePanelWebView(
911
Browser* browser,
1012
SidePanelEntryScope& scope,
@@ -20,3 +22,6 @@ PlaylistSidePanelWebView::~PlaylistSidePanelWebView() = default;
2022
base::WeakPtr<PlaylistSidePanelWebView> PlaylistSidePanelWebView::GetWeakPtr() {
2123
return weak_ptr_factory_.GetWeakPtr();
2224
}
25+
26+
BEGIN_METADATA(PlaylistSidePanelWebView)
27+
END_METADATA

browser/ui/views/side_panel/playlist/playlist_side_panel_web_view.h

+3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ class SidePanelEntryScope;
1313
#include "base/functional/callback_forward.h"
1414
#include "brave/browser/ui/webui/playlist_ui.h"
1515
#include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h"
16+
#include "ui/base/metadata/metadata_header_macros.h"
1617

1718
class PlaylistSidePanelWebView : public SidePanelWebUIView {
19+
METADATA_HEADER(PlaylistSidePanelWebView, SidePanelWebUIView)
20+
1821
public:
1922
PlaylistSidePanelWebView(Browser* browser,
2023
SidePanelEntryScope& scope,

0 commit comments

Comments
 (0)