blob: 3804cfed65a311149c962510d227d562a5503765 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/bubble/webui_bubble_manager.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_dialog_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/webui/top_chrome/top_chrome_web_ui_controller.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/spare_render_process_host_manager.h"
#include "content/public/test/browser_test.h"
namespace {
// Close the bubble, clear any cached content wrapper, including the ones
// stored in the contents wrapper service.
void DestroyBubble(WebUIBubbleManager* bubble_manager, Profile* profile) {
bubble_manager->CloseBubble();
bubble_manager->ResetContentsWrapperForTesting();
base::RunLoop().RunUntilIdle();
}
} // namespace
class TestWebUIController : public TopChromeWebUIController {
WEB_UI_CONTROLLER_TYPE_DECL();
};
WEB_UI_CONTROLLER_TYPE_IMPL(TestWebUIController)
template <>
class WebUIContentsWrapperT<TestWebUIController> final
: public WebUIContentsWrapper {
public:
WebUIContentsWrapperT(const GURL& webui_url,
Profile* profile,
int task_manager_string_id,
bool webui_resizes_host = true,
bool esc_closes_ui = true,
bool supports_draggable_regions = false)
: WebUIContentsWrapper(webui_url,
profile,
task_manager_string_id,
webui_resizes_host,
esc_closes_ui,
supports_draggable_regions,
"Test") {}
void ReloadWebContents() override {}
base::WeakPtr<WebUIContentsWrapper> GetWeakPtr() override {
return weak_ptr_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<WebUIContentsWrapper> weak_ptr_factory_{this};
};
class WebUIBubbleManagerBrowserTest : public InProcessBrowserTest {
public:
WebUIBubbleManagerBrowserTest() = default;
WebUIBubbleManagerBrowserTest(const WebUIBubbleManagerBrowserTest&) = delete;
const WebUIBubbleManagerBrowserTest& operator=(
const WebUIBubbleManagerBrowserTest&) = delete;
~WebUIBubbleManagerBrowserTest() override = default;
// content::BrowserTestBase:
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
bubble_manager_ = MakeBubbleManager();
}
void TearDownOnMainThread() override {
bubble_manager_.reset();
InProcessBrowserTest::TearDownOnMainThread();
}
WebUIBubbleManager* bubble_manager() { return bubble_manager_.get(); }
// WebContents under the ".top-chrome" pseudo-TLD will reuse the render
// process.
std::unique_ptr<WebUIBubbleManager> MakeBubbleManager(
GURL site_url = GURL("chrome://test.top-chrome")) {
return WebUIBubbleManager::Create<TestWebUIController>(
BrowserView::GetBrowserViewForBrowser(browser()), browser(), site_url,
1);
}
void DestroyBubbleManager() { bubble_manager_.reset(); }
private:
std::unique_ptr<WebUIBubbleManager> bubble_manager_;
};
IN_PROC_BROWSER_TEST_F(WebUIBubbleManagerBrowserTest, CreateAndCloseBubble) {
EXPECT_EQ(nullptr, bubble_manager()->GetBubbleWidget());
bubble_manager()->ShowBubble();
EXPECT_NE(nullptr, bubble_manager()->GetBubbleWidget());
EXPECT_FALSE(bubble_manager()->GetBubbleWidget()->IsClosed());
bubble_manager()->CloseBubble();
EXPECT_TRUE(bubble_manager()->GetBubbleWidget()->IsClosed());
}
IN_PROC_BROWSER_TEST_F(WebUIBubbleManagerBrowserTest,
ShowUISetsBubbleWidgetVisible) {
EXPECT_EQ(nullptr, bubble_manager()->GetBubbleWidget());
bubble_manager()->ShowBubble();
EXPECT_NE(nullptr, bubble_manager()->GetBubbleWidget());
EXPECT_FALSE(bubble_manager()->GetBubbleWidget()->IsClosed());
EXPECT_FALSE(bubble_manager()->GetBubbleWidget()->IsVisible());
bubble_manager()->bubble_view_for_testing()->ShowUI();
EXPECT_TRUE(bubble_manager()->GetBubbleWidget()->IsVisible());
bubble_manager()->CloseBubble();
EXPECT_TRUE(bubble_manager()->GetBubbleWidget()->IsClosed());
}
// Ensures that the WebUI bubble is destroyed synchronously with the manager.
// This guards against a potential UAF crash (see crbug.com/1345546).
IN_PROC_BROWSER_TEST_F(WebUIBubbleManagerBrowserTest,
ManagerDestructionClosesBubble) {
EXPECT_EQ(nullptr, bubble_manager()->GetBubbleWidget());
bubble_manager()->ShowBubble();
EXPECT_NE(nullptr, bubble_manager()->GetBubbleWidget());
base::WeakPtr<WebUIBubbleDialogView> bubble_view =
bubble_manager()->bubble_view_for_testing();
EXPECT_TRUE(bubble_view);
bubble_view->ShowUI();
EXPECT_TRUE(bubble_manager()->GetBubbleWidget()->IsVisible());
// Destroy the bubble manager without explicitly destroying the bubble. Ensure
// the bubble is closed synchronously.
DestroyBubbleManager();
EXPECT_FALSE(bubble_view);
}
// Verifies that the warm-up levels are correctly recorded.
// TODO(crbug.com/325316150): Fix flakiness and re-enable.
IN_PROC_BROWSER_TEST_F(WebUIBubbleManagerBrowserTest, DISABLED_WarmupLevel) {
// Use the spare renderer if there is one.
auto& spare_manager = content::SpareRenderProcessHostManager::Get();
EXPECT_FALSE(spare_manager.GetSpares().empty());
bubble_manager()->ShowBubble();
EXPECT_EQ(bubble_manager()->contents_warmup_level(),
WebUIContentsWarmupLevel::kSpareRenderer);
// Create a new process if there is no spare renderer.
spare_manager.CleanupSparesForTesting();
DestroyBubble(bubble_manager(), browser()->profile());
bubble_manager()->ShowBubble();
EXPECT_EQ(bubble_manager()->contents_warmup_level(),
WebUIContentsWarmupLevel::kNoRenderer);
// Use the process dedicated to top chrome WebUIs if there is one.
DestroyBubble(bubble_manager(), browser()->profile());
// Use a different domain under .top-chrome so that the WebContents
// is not reused if WebUIBubblePerProfilePersistence is enabled.
std::unique_ptr<WebUIBubbleManager> another_bubble_manager =
MakeBubbleManager(GURL("chrome://test2.top-chrome"));
another_bubble_manager->ShowBubble();
bubble_manager()->ShowBubble();
EXPECT_EQ(bubble_manager()->contents_warmup_level(),
WebUIContentsWarmupLevel::kDedicatedRenderer);
// Use the cached WebContents if there is one.
bubble_manager()->CloseBubble();
base::RunLoop().RunUntilIdle();
bubble_manager()->ShowBubble();
EXPECT_EQ(bubble_manager()->contents_warmup_level(),
WebUIContentsWarmupLevel::kReshowingWebContents);
}
IN_PROC_BROWSER_TEST_F(WebUIBubbleManagerBrowserTest,
BrowserWindowContextSetOnShow) {
EXPECT_EQ(nullptr, bubble_manager()->GetBubbleWidget());
bubble_manager()->ShowBubble();
EXPECT_TRUE(bubble_manager()->GetBubbleWidget());
EXPECT_EQ(
browser(),
webui::GetBrowserWindowInterface(
bubble_manager()->GetContentsWrapperForTesting()->web_contents()));
}