Commit 6e50740c authored by Thomas Lukaszewicz's avatar Thomas Lukaszewicz Committed by Commit Bot

Add WebUI bubble classes to c/b/ui/views

This CL adds the following classes to c/b/ui/views:
 - WebUIBubbleView
     A WebView subclass that supports the ShowUI() call from
     its hosting WebUI.

 - WebUIBubbleManager
     Takes a WebUI and GURL and manages the showing / closing of the
     WebUIBubbleDIalogView and caching of the hosted WebUIBubbleView.
     Example included in this CL is management of read later's
     WebUI.

 - WebUIBubbleDialogView
     Responsible for hosting the WebView in an anchored Views bubble.

Follow up will cleanup existing WebUI bubble code from
ui/views/controls/webview and switch Tab Search to use the
WebUIBubbleManager.

Bug: 1099917
Change-Id: I8a70394fe1718f31727c9a9d2478ea860890bcb7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2501075
Commit-Queue: Thomas Lukaszewicz <tluk@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822754}
parent 69f1505a
...@@ -3443,6 +3443,12 @@ static_library("ui") { ...@@ -3443,6 +3443,12 @@ static_library("ui") {
"views/bookmarks/bookmark_menu_delegate.h", "views/bookmarks/bookmark_menu_delegate.h",
"views/browser_commands_views.cc", "views/browser_commands_views.cc",
"views/browser_dialogs_views.cc", "views/browser_dialogs_views.cc",
"views/bubble/webui_bubble_dialog_view.cc",
"views/bubble/webui_bubble_dialog_view.h",
"views/bubble/webui_bubble_manager.cc",
"views/bubble/webui_bubble_manager.h",
"views/bubble/webui_bubble_view.cc",
"views/bubble/webui_bubble_view.h",
"views/bubble_anchor_util_views.cc", "views/bubble_anchor_util_views.cc",
"views/bubble_anchor_util_views.h", "views/bubble_anchor_util_views.h",
"views/bubble_menu_item_factory.cc", "views/bubble_menu_item_factory.cc",
...@@ -3875,8 +3881,6 @@ static_library("ui") { ...@@ -3875,8 +3881,6 @@ static_library("ui") {
"views/qrcode_generator/qrcode_generator_bubble.h", "views/qrcode_generator/qrcode_generator_bubble.h",
"views/qrcode_generator/qrcode_generator_icon_view.cc", "views/qrcode_generator/qrcode_generator_icon_view.cc",
"views/qrcode_generator/qrcode_generator_icon_view.h", "views/qrcode_generator/qrcode_generator_icon_view.h",
"views/read_later/read_later_bubble_view.cc",
"views/read_later/read_later_bubble_view.h",
"views/read_later/read_later_button.cc", "views/read_later/read_later_button.cc",
"views/read_later/read_later_button.h", "views/read_later/read_later_button.h",
"views/reader_mode/reader_mode_icon_view.cc", "views/reader_mode/reader_mode_icon_view.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_dialog_view.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
namespace {
// The min / max size available to the WebBubbleDialogView.
// These are arbitrary sizes that match those set by ExtensionPopup.
// TODO(tluk): Determine the correct size constraints for the
// WebBubbleDialogView.
constexpr gfx::Size kMinSize(25, 25);
constexpr gfx::Size kMaxSize(800, 600);
} // namespace
// static.
base::WeakPtr<WebUIBubbleDialogView>
WebUIBubbleDialogView::CreateWebUIBubbleDialog(
std::unique_ptr<WebUIBubbleDialogView> bubble_view) {
auto weak_ptr = bubble_view->weak_factory_.GetWeakPtr();
BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
return weak_ptr;
}
WebUIBubbleDialogView::WebUIBubbleDialogView(
views::View* anchor_view,
std::unique_ptr<WebUIBubbleView> web_view)
: BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
web_view_(AddChildView(std::move(web_view))) {
web_view_->set_host(this);
SetButtons(ui::DIALOG_BUTTON_NONE);
set_margins(gfx::Insets());
SetLayoutManager(std::make_unique<views::FillLayout>());
}
WebUIBubbleDialogView::~WebUIBubbleDialogView() = default;
std::unique_ptr<WebUIBubbleView> WebUIBubbleDialogView::RemoveWebView() {
DCHECK(web_view_);
auto* web_view = web_view_;
web_view_ = nullptr;
web_view->set_host(nullptr);
return RemoveChildViewT(web_view);
}
gfx::Size WebUIBubbleDialogView::CalculatePreferredSize() const {
// Constrain the size to popup min/max.
gfx::Size preferred_size = BubbleDialogDelegateView::CalculatePreferredSize();
preferred_size.SetToMax(kMinSize);
preferred_size.SetToMin(kMaxSize);
return preferred_size;
}
void WebUIBubbleDialogView::AddedToWidget() {
BubbleDialogDelegateView::AddedToWidget();
web_view_->holder()->SetCornerRadii(gfx::RoundedCornersF(GetCornerRadius()));
}
void WebUIBubbleDialogView::ShowUI() {
SetVisible(true);
DCHECK(GetWidget());
GetWidget()->Show();
web_view_->GetWebContents()->Focus();
}
void WebUIBubbleDialogView::OnWebViewSizeChanged() {
SizeToContents();
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_DIALOG_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_DIALOG_VIEW_H_
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_view.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
// A Views bubble host for a WebUIBubbleView.
class WebUIBubbleDialogView : public views::BubbleDialogDelegateView,
public WebUIBubbleView::Host {
public:
static base::WeakPtr<WebUIBubbleDialogView> CreateWebUIBubbleDialog(
std::unique_ptr<WebUIBubbleDialogView> bubble_view);
WebUIBubbleDialogView(views::View* anchor_view,
std::unique_ptr<WebUIBubbleView> web_view);
WebUIBubbleDialogView(const WebUIBubbleDialogView&) = delete;
WebUIBubbleDialogView& operator=(const WebUIBubbleDialogView&) = delete;
~WebUIBubbleDialogView() override;
std::unique_ptr<WebUIBubbleView> RemoveWebView();
WebUIBubbleView* web_view() { return web_view_; }
// BubbleDialogDelegateView:
gfx::Size CalculatePreferredSize() const override;
void AddedToWidget() override;
// WebUIBubbleView::Host:
void ShowUI() override;
void OnWebViewSizeChanged() override;
private:
WebUIBubbleView* web_view_ = nullptr;
base::WeakPtrFactory<WebUIBubbleDialogView> weak_factory_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_DIALOG_VIEW_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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_dialog_view.h"
#include <memory>
#include <utility>
#include "chrome/browser/ui/views/bubble/webui_bubble_view.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/unique_widget_ptr.h"
namespace views {
namespace test {
class WebUIBubbleDialogViewTest : public ChromeViewsTestBase {
public:
WebUIBubbleDialogViewTest() = default;
WebUIBubbleDialogViewTest(const WebUIBubbleDialogViewTest&) = delete;
WebUIBubbleDialogViewTest& operator=(const WebUIBubbleDialogViewTest&) =
delete;
~WebUIBubbleDialogViewTest() override = default;
// ChromeViewsTestBase:
void SetUp() override {
ChromeViewsTestBase::SetUp();
profile_ = std::make_unique<TestingProfile>();
anchor_widget_ = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
anchor_widget_->Init(std::move(params));
auto bubble_view = std::make_unique<WebUIBubbleDialogView>(
anchor_widget_->GetContentsView(),
std::make_unique<WebUIBubbleView>(profile_.get()));
bubble_view_ = bubble_view.get();
bubble_widget_ =
BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
}
void TearDown() override {
bubble_widget_->CloseNow();
anchor_widget_.reset();
ChromeViewsTestBase::TearDown();
}
protected:
WebUIBubbleDialogView* bubble_dialog_view() { return bubble_view_; }
Widget* bubble_widget() { return bubble_widget_; }
private:
std::unique_ptr<TestingProfile> profile_;
views::UniqueWidgetPtr anchor_widget_;
Widget* bubble_widget_ = nullptr;
WebUIBubbleDialogView* bubble_view_ = nullptr;
};
TEST_F(WebUIBubbleDialogViewTest, BubbleRespondsToWebViewPreferredSizeChanges) {
views::WebView* const web_view = bubble_dialog_view()->web_view();
constexpr gfx::Size web_view_initial_size(100, 100);
web_view->SetPreferredSize(gfx::Size(100, 100));
bubble_dialog_view()->OnWebViewSizeChanged();
const gfx::Size widget_initial_size =
bubble_widget()->GetWindowBoundsInScreen().size();
// The bubble should be at least as big as the webview.
EXPECT_GE(widget_initial_size.width(), web_view_initial_size.width());
EXPECT_GE(widget_initial_size.height(), web_view_initial_size.height());
// Resize the webview.
constexpr gfx::Size web_view_final_size(200, 200);
web_view->SetPreferredSize(web_view_final_size);
bubble_dialog_view()->OnWebViewSizeChanged();
// Ensure the bubble resizes as expected.
const gfx::Size widget_final_size =
bubble_widget()->GetWindowBoundsInScreen().size();
EXPECT_LT(widget_initial_size.width(), widget_final_size.width());
EXPECT_LT(widget_initial_size.height(), widget_final_size.height());
// The bubble should be at least as big as the webview.
EXPECT_GE(widget_final_size.width(), web_view_final_size.width());
EXPECT_GE(widget_final_size.height(), web_view_final_size.height());
}
TEST_F(WebUIBubbleDialogViewTest, RemoveWebViewPassesWebView) {
// The dialog should initially have a WebView after setup.
views::WebView* const dialog_web_view = bubble_dialog_view()->web_view();
EXPECT_NE(nullptr, dialog_web_view);
// RemoveWebView should pass back the WebView used by the dialog.
std::unique_ptr<views::WebView> removed_web_view =
bubble_dialog_view()->RemoveWebView();
EXPECT_EQ(dialog_web_view, removed_web_view.get());
// The bubble should not hold on to any old WebView pointer.
EXPECT_EQ(nullptr, bubble_dialog_view()->web_view());
}
} // namespace test
} // namespace views
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/timer/timer.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_dialog_view.h"
#include "ui/views/widget/widget.h"
namespace {
constexpr base::TimeDelta kWebViewRetentionTime =
base::TimeDelta::FromSeconds(30);
} // namespace
WebUIBubbleManagerBase::WebUIBubbleManagerBase(
views::View* anchor_view,
content::BrowserContext* browser_context,
const GURL& webui_url)
: anchor_view_(anchor_view),
browser_context_(browser_context),
webui_url_(webui_url),
cache_timer_(std::make_unique<base::RetainingOneShotTimer>(
FROM_HERE,
kWebViewRetentionTime,
base::BindRepeating(&WebUIBubbleManagerBase::ResetWebView,
base::Unretained(this)))) {}
WebUIBubbleManagerBase::~WebUIBubbleManagerBase() = default;
bool WebUIBubbleManagerBase::ShowBubble() {
if (bubble_view_)
return false;
cache_timer_->Stop();
if (cached_web_view_) {
cached_web_view_->GetWebContents()->ReloadFocusedFrame();
} else {
cached_web_view_ = CreateWebView();
}
bubble_view_ = WebUIBubbleDialogView::CreateWebUIBubbleDialog(
std::make_unique<WebUIBubbleDialogView>(anchor_view_,
std::move(cached_web_view_)));
observed_bubble_widget_.Add(bubble_view_->GetWidget());
return true;
}
void WebUIBubbleManagerBase::CloseBubble() {
if (!bubble_view_)
return;
DCHECK(bubble_view_->GetWidget());
bubble_view_->GetWidget()->CloseWithReason(
views::Widget::ClosedReason::kUnspecified);
}
views::Widget* WebUIBubbleManagerBase::GetBubbleWidget() const {
return bubble_view_ ? bubble_view_->GetWidget() : nullptr;
}
void WebUIBubbleManagerBase::OnWidgetDestroying(views::Widget* widget) {
DCHECK(bubble_view_);
DCHECK_EQ(bubble_view_->GetWidget(), widget);
cached_web_view_ = bubble_view_->RemoveWebView();
observed_bubble_widget_.Remove(bubble_view_->GetWidget());
cache_timer_->Reset();
}
void WebUIBubbleManagerBase::ResetWebView() {
cached_web_view_.reset();
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_MANAGER_H_
#define CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_MANAGER_H_
#include <memory>
#include <utility>
#include "base/scoped_observer.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_view.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
namespace content {
class BrowserContext;
} // namespace content
class GURL;
class WebUIBubbleDialogView;
// WebUIBubbleManagerBase handles the creation / destruction of the WebUI bubble
// and caching of the WebView.
class WebUIBubbleManagerBase : public views::WidgetObserver {
public:
WebUIBubbleManagerBase(views::View* anchor_view,
content::BrowserContext* browser_context,
const GURL& webui_url);
WebUIBubbleManagerBase(const WebUIBubbleManagerBase&) = delete;
const WebUIBubbleManagerBase& operator=(const WebUIBubbleManagerBase&) =
delete;
~WebUIBubbleManagerBase() override;
bool ShowBubble();
void CloseBubble();
views::Widget* GetBubbleWidget() const;
// views::WidgetObserver:
void OnWidgetDestroying(views::Widget* widget) override;
content::BrowserContext* browser_context() { return browser_context_; }
const GURL& webui_url() const { return webui_url_; }
private:
virtual std::unique_ptr<WebUIBubbleView> CreateWebView() = 0;
void ResetWebView();
views::View* anchor_view_;
content::BrowserContext* browser_context_;
GURL webui_url_;
base::WeakPtr<WebUIBubbleDialogView> bubble_view_;
// A cached WebView used to make re-triggering the UI faster. This is not set
// when the bubble is showing. It will only be set when the bubble is
// not showing. It is only retained for the length of the |cache_timer_|.
std::unique_ptr<WebUIBubbleView> cached_web_view_;
// A timer controlling how long the |cached_web_view_| is cached for.
std::unique_ptr<base::RetainingOneShotTimer> cache_timer_;
ScopedObserver<views::Widget, views::WidgetObserver> observed_bubble_widget_{
this};
};
template <typename T>
class WebUIBubbleManager : public WebUIBubbleManagerBase {
public:
using WebUIBubbleManagerBase::WebUIBubbleManagerBase;
private:
std::unique_ptr<WebUIBubbleView> CreateWebView() override {
auto web_view = std::make_unique<WebUIBubbleView>(browser_context());
web_view->template LoadURL<T>(webui_url());
return web_view;
}
};
#endif // CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_MANAGER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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 "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
namespace {
class TestWebUIBubbleManager : public WebUIBubbleManagerBase {
public:
explicit TestWebUIBubbleManager(Browser* browser)
: WebUIBubbleManagerBase(BrowserView::GetBrowserViewForBrowser(browser),
browser->profile(),
GURL("chrome://about")) {}
TestWebUIBubbleManager(const TestWebUIBubbleManager&) = delete;
const TestWebUIBubbleManager& operator=(const TestWebUIBubbleManager&) =
delete;
~TestWebUIBubbleManager() override = default;
private:
std::unique_ptr<WebUIBubbleView> CreateWebView() override {
return std::make_unique<WebUIBubbleView>(browser_context());
}
};
} // namespace
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_ = std::make_unique<TestWebUIBubbleManager>(browser());
}
TestWebUIBubbleManager* bubble_manager() { return bubble_manager_.get(); }
private:
std::unique_ptr<TestWebUIBubbleManager> bubble_manager_;
};
IN_PROC_BROWSER_TEST_F(WebUIBubbleManagerBrowserTest, CreateAndClose) {
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());
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_view.h"
#include "content/public/browser/keyboard_event_processing_result.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/widget/widget.h"
namespace {
bool IsEscapeEvent(const content::NativeWebKeyboardEvent& event) {
return event.GetType() ==
content::NativeWebKeyboardEvent::Type::kRawKeyDown &&
event.windows_key_code == ui::VKEY_ESCAPE;
}
// The min / max size available to the WebUIBubbleView.
// These are arbitrary sizes that match those set by ExtensionPopup.
// TODO(tluk): Determine the correct size constraints for the
// WebUIBubbleView.
constexpr gfx::Size kMinSize(25, 25);
constexpr gfx::Size kMaxSize(800, 600);
} // namespace
WebUIBubbleView::WebUIBubbleView(content::BrowserContext* browser_context)
: WebView(browser_context) {
EnableSizingFromWebContents(kMinSize, kMaxSize);
SetVisible(false);
// Allow the embedder to handle accelerators not handled by the WebContents.
set_allow_accelerators(true);
}
WebUIBubbleView::~WebUIBubbleView() = default;
void WebUIBubbleView::PreferredSizeChanged() {
WebView::PreferredSizeChanged();
if (host_)
host_->OnWebViewSizeChanged();
}
bool WebUIBubbleView::HandleContextMenu(
content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) {
// Ignores context menu.
return true;
}
content::KeyboardEventProcessingResult WebUIBubbleView::PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
// Close the bubble if an escape event is detected. Handle this here to
// prevent the renderer from capturing the event and not propagating it up.
if (IsEscapeEvent(event) && GetWidget()) {
GetWidget()->CloseWithReason(views::Widget::ClosedReason::kEscKeyPressed);
return content::KeyboardEventProcessingResult::HANDLED;
}
return content::KeyboardEventProcessingResult::NOT_HANDLED;
}
bool WebUIBubbleView::HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
event, GetFocusManager());
}
void WebUIBubbleView::ShowUI() {
if (host_)
host_->ShowUI();
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_VIEW_H_
#include <memory>
#include <utility>
#include "base/memory/weak_ptr.h"
#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/webui/mojo_bubble_web_ui_controller.h"
namespace content {
class BrowserContext;
} // namespace content
// WebUIBubbleView is used to host WebUI that notifies its host when rendering
// has finished by calling Host::ShowUI(). Similarly WebUIBubbleView notifies
// its host of WebUI size changes by calling Host::OnWebViewSizeChanged().
class WebUIBubbleView : public views::WebView,
public ui::MojoBubbleWebUIController::Embedder {
public:
class Host {
public:
virtual void ShowUI() = 0;
virtual void OnWebViewSizeChanged() = 0;
};
explicit WebUIBubbleView(content::BrowserContext* browser_context);
~WebUIBubbleView() override;
// The type T enables WebUIBubbleView to know what WebUIController is being
// used for the hosted WebUI and allows it to make sure the associated WebUI
// is a MojoBubbleWebUIController at compile time.
template <typename T>
void LoadURL(const GURL& url) {
// Lie to WebContents so it starts rendering and eventually calls ShowUI().
GetWebContents()->WasShown();
SetVisible(true);
LoadInitialURL(url);
T* async_webui_controller =
GetWebContents()->GetWebUI()->GetController()->template GetAs<T>();
// Depends on the WebUIController object being constructed synchronously
// when the navigation is started in LoadInitialURL().
async_webui_controller->set_embedder(weak_ptr_factory_.GetWeakPtr());
}
void set_host(Host* host) { host_ = host; }
// WebView:
void PreferredSizeChanged() override;
bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) override;
content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override;
bool HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override;
// MojoBubbleWebUIController::Embedder:
void ShowUI() override;
private:
// |host_| does not always have to be set. The WebUIBubbleView can be cached
// and running in the background without a host being present.
Host* host_ = nullptr;
// A handler to handle unhandled keyboard messages coming back from the
// renderer process.
views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
base::WeakPtrFactory<WebUIBubbleView> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_BUBBLE_WEBUI_BUBBLE_VIEW_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_view.h"
#include <memory>
#include <utility>
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/site_instance.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/unique_widget_ptr.h"
namespace views {
namespace {
class MockHost : public WebUIBubbleView::Host {
public:
void ShowUI() override { ++show_ui_called_; }
void OnWebViewSizeChanged() override { ++size_changed_called_; }
int show_ui_called() const { return show_ui_called_; }
int size_changed_called() const { return size_changed_called_; }
private:
int show_ui_called_ = 0;
int size_changed_called_ = 0;
};
} // namespace
namespace test {
class WebUIBubbleViewTest : public ChromeViewsTestBase {
public:
WebUIBubbleViewTest() = default;
WebUIBubbleViewTest(const WebUIBubbleViewTest&) = delete;
WebUIBubbleViewTest& operator=(const WebUIBubbleViewTest&) = delete;
~WebUIBubbleViewTest() override = default;
// ViewsTestBase:
void SetUp() override {
ChromeViewsTestBase::SetUp();
profile_ = std::make_unique<TestingProfile>();
scoped_refptr<content::SiteInstance> instance =
content::SiteInstance::Create(profile_.get());
instance->GetProcess()->Init();
test_contents_ = content::WebContentsTester::CreateTestWebContents(
profile_.get(), std::move(instance));
widget_ = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
widget_->Init(std::move(params));
auto web_view = std::make_unique<WebUIBubbleView>(profile_.get());
web_view->SetWebContents(test_contents_.get());
test_contents_->SetDelegate(web_view.get());
web_view->set_host(&host_);
web_view_ = widget_->GetContentsView()->AddChildView(std::move(web_view));
}
void TearDown() override {
widget_.reset();
ChromeViewsTestBase::TearDown();
}
WebUIBubbleView* web_view() { return web_view_; }
Widget* widget() { return widget_.get(); }
const MockHost& host() { return host_; }
private:
content::RenderViewHostTestEnabler test_render_host_factories_;
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<content::WebContents> test_contents_;
views::UniqueWidgetPtr widget_;
WebUIBubbleView* web_view_ = nullptr;
MockHost host_;
};
TEST_F(WebUIBubbleViewTest, EscapeKeyClosesWidget) {
content::NativeWebKeyboardEvent event(
blink::WebInputEvent::Type::kRawKeyDown,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
event.windows_key_code = ui::VKEY_ESCAPE;
EXPECT_FALSE(widget()->IsClosed());
web_view()
->GetWebContents()
->GetRenderWidgetHostView()
->GetRenderWidgetHost()
->ForwardKeyboardEvent(event);
EXPECT_TRUE(widget()->IsClosed());
}
TEST_F(WebUIBubbleViewTest, PreferredSizeChangesNotifiesHost) {
EXPECT_EQ(0, host().size_changed_called());
constexpr gfx::Size new_size(10, 10);
EXPECT_NE(web_view()->GetPreferredSize(), new_size);
web_view()->SetPreferredSize(new_size);
EXPECT_EQ(1, host().size_changed_called());
}
TEST_F(WebUIBubbleViewTest, ShowUINotifiesHost) {
EXPECT_EQ(0, host().show_ui_called());
web_view()->ShowUI();
EXPECT_EQ(1, host().show_ui_called());
}
} // namespace test
} // namespace views
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/read_later/read_later_bubble_view.h"
#include "base/strings/string16.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/side_panel.h"
#include "chrome/common/webui_url_constants.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/fill_layout.h"
#include "chrome/browser/ui/webui/read_later/read_later_ui.h"
// ReadLaterBubbleView ---------------------------------------------------------
// static
base::WeakPtr<ReadLaterBubbleView> ReadLaterBubbleView::Show(
const Browser* browser,
views::View* anchor_view) {
auto bubble_view =
std::make_unique<ReadLaterBubbleView>(browser, anchor_view);
auto weak_ptr = bubble_view->weak_factory_.GetWeakPtr();
BrowserView* const browser_view =
BrowserView::GetBrowserViewForBrowser(browser);
if (browser_view->side_panel()) {
bubble_view->LoadURL<ReadLaterUI>(GURL(chrome::kChromeUIReadLaterURL));
browser_view->side_panel()->AddContent(std::move(bubble_view));
} else {
views::WebBubbleDialogView::CreateWebBubbleDialog<ReadLaterUI>(
std::move(bubble_view), GURL(chrome::kChromeUIReadLaterURL));
}
return weak_ptr;
}
ReadLaterBubbleView::ReadLaterBubbleView(const Browser* browser,
views::View* anchor_view)
: WebBubbleDialogView(browser->profile(), anchor_view) {}
ReadLaterBubbleView::~ReadLaterBubbleView() = default;
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUBBLE_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUBBLE_VIEW_H_
#include <memory>
#include "components/reading_list/core/reading_list_model_observer.h"
#include "ui/views/controls/webview/web_bubble_dialog_view.h"
class Browser;
// This bubble view displays a list of read-later entries.
// This class is only used with the kReadLater feature.
class ReadLaterBubbleView : public views::WebBubbleDialogView {
public:
// Displays the read-later dialog under |anchor_view|, attached to |browser|.
static base::WeakPtr<ReadLaterBubbleView> Show(const Browser* browser,
views::View* anchor_view);
ReadLaterBubbleView(const Browser* browser, views::View* anchor_view);
ReadLaterBubbleView(const ReadLaterBubbleView&) = delete;
ReadLaterBubbleView& operator=(const ReadLaterBubbleView&) = delete;
~ReadLaterBubbleView() override;
private:
base::WeakPtrFactory<ReadLaterBubbleView> weak_factory_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUBBLE_VIEW_H_
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_dialog_view.h"
#include "chrome/browser/ui/views/bubble/webui_bubble_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/read_later/read_later_bubble_view.h"
#include "chrome/browser/ui/views/side_panel.h" #include "chrome/browser/ui/views/side_panel.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h" #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/pointer/touch_ui_controller.h" #include "ui/base/pointer/touch_ui_controller.h"
...@@ -26,7 +28,11 @@ ...@@ -26,7 +28,11 @@
ReadLaterButton::ReadLaterButton(Browser* browser) ReadLaterButton::ReadLaterButton(Browser* browser)
: ToolbarButton(base::BindRepeating(&ReadLaterButton::ButtonPressed, : ToolbarButton(base::BindRepeating(&ReadLaterButton::ButtonPressed,
base::Unretained(this))), base::Unretained(this))),
browser_(browser) { browser_(browser),
webui_bubble_manager_(std::make_unique<WebUIBubbleManager<ReadLaterUI>>(
this,
browser->profile(),
GURL(chrome::kChromeUIReadLaterURL))) {
SetTooltipText(l10n_util::GetStringUTF16(IDS_READ_LATER_TITLE)); SetTooltipText(l10n_util::GetStringUTF16(IDS_READ_LATER_TITLE));
GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kMenu); GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kMenu);
button_controller()->set_notify_action( button_controller()->set_notify_action(
...@@ -61,19 +67,28 @@ int ReadLaterButton::GetIconSize() const { ...@@ -61,19 +67,28 @@ int ReadLaterButton::GetIconSize() const {
void ReadLaterButton::ButtonPressed() { void ReadLaterButton::ButtonPressed() {
BrowserView* const browser_view = BrowserView* const browser_view =
BrowserView::GetBrowserViewForBrowser(browser_); BrowserView::GetBrowserViewForBrowser(browser_);
if (read_later_bubble_) {
if (browser_view->side_panel()) { if (browser_view->side_panel()) {
browser_view->side_panel()->RemoveContent(read_later_bubble_.get()); if (!read_later_side_panel_bubble_) {
DCHECK(!read_later_bubble_); browser_view->side_panel()->RemoveContent(read_later_side_panel_bubble_);
// TODO(pbos): Observe read_later_bubble_ so we don't need to read_later_side_panel_bubble_ = nullptr;
// TODO(pbos): Observe read_later_side_panel_bubble_ so we don't need to
// SetHighlighted(false) here. // SetHighlighted(false) here.
SetHighlighted(false); SetHighlighted(false);
} else { } else {
read_later_bubble_->GetWidget()->Close(); auto web_view = std::make_unique<WebUIBubbleView>(browser_->profile());
web_view->LoadURL<ReadLaterUI>(GURL(chrome::kChromeUIReadLaterURL));
auto bubble_view =
std::make_unique<WebUIBubbleDialogView>(this, std::move(web_view));
read_later_side_panel_bubble_ = bubble_view.get();
browser_view->side_panel()->AddContent(std::move(bubble_view));
SetHighlighted(true);
} }
} else { } else {
read_later_bubble_ = ReadLaterBubbleView::Show(browser_, this); if (webui_bubble_manager_->GetBubbleWidget()) {
if (browser_view->side_panel()) webui_bubble_manager_->CloseBubble();
SetHighlighted(true); } else {
webui_bubble_manager_->ShowBubble();
}
} }
} }
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
#ifndef CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUTTON_H_ #ifndef CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUTTON_H_
#define CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUTTON_H_ #define CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUTTON_H_
#include "chrome/browser/ui/views/bubble/webui_bubble_manager.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h" #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/browser/ui/webui/read_later/read_later_ui.h"
class Browser; class Browser;
class ReadLaterBubbleView; class WebUIBubbleDialogView;
// Button in the bookmarks bar that provides access to the corresponding // Button in the bookmarks bar that provides access to the corresponding
// read later menu. // read later menu.
...@@ -24,18 +26,17 @@ class ReadLaterButton : public ToolbarButton { ...@@ -24,18 +26,17 @@ class ReadLaterButton : public ToolbarButton {
const char* GetClassName() const override; const char* GetClassName() const override;
void UpdateIcon() override; void UpdateIcon() override;
base::WeakPtr<ReadLaterBubbleView> read_later_bubble_for_testing() {
return read_later_bubble_;
}
private: private:
int GetIconSize() const; int GetIconSize() const;
void ButtonPressed(); void ButtonPressed();
base::WeakPtr<ReadLaterBubbleView> read_later_bubble_;
Browser* const browser_; Browser* const browser_;
// TODO(pbos): Figure out a better way to handle this.
WebUIBubbleDialogView* read_later_side_panel_bubble_ = nullptr;
std::unique_ptr<WebUIBubbleManager<ReadLaterUI>> webui_bubble_manager_;
}; };
#endif // CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUTTON_H_ #endif // CHROME_BROWSER_UI_VIEWS_READ_LATER_READ_LATER_BUTTON_H_
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/ui/views/read_later/read_later_bubble_view.h" #include "chrome/browser/ui/views/read_later/read_later_button.h"
#include <string> #include <string>
...@@ -14,14 +14,13 @@ ...@@ -14,14 +14,13 @@
#include "chrome/browser/ui/test/test_browser_dialog.h" #include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/read_later/read_later_button.h"
#include "components/bookmarks/common/bookmark_pref_names.h" #include "components/bookmarks/common/bookmark_pref_names.h"
#include "components/reading_list/features/reading_list_switches.h" #include "components/reading_list/features/reading_list_switches.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
class ReadLaterBubbleViewBrowserTest : public DialogBrowserTest { class ReadLaterButtonBrowserTest : public DialogBrowserTest {
public: public:
ReadLaterBubbleViewBrowserTest() { ReadLaterButtonBrowserTest() {
feature_list_.InitAndEnableFeature(reading_list::switches::kReadLater); feature_list_.InitAndEnableFeature(reading_list::switches::kReadLater);
} }
...@@ -41,7 +40,6 @@ class ReadLaterBubbleViewBrowserTest : public DialogBrowserTest { ...@@ -41,7 +40,6 @@ class ReadLaterBubbleViewBrowserTest : public DialogBrowserTest {
void ShowUi(const std::string& name) override { void ShowUi(const std::string& name) override {
ASSERT_TRUE(browser()->bookmark_bar_state() == BookmarkBar::SHOW); ASSERT_TRUE(browser()->bookmark_bar_state() == BookmarkBar::SHOW);
ClickReadLaterButton(); ClickReadLaterButton();
ASSERT_TRUE(GetReadLaterButton(browser())->read_later_bubble_for_testing());
} }
void ClickReadLaterButton() { void ClickReadLaterButton() {
...@@ -52,7 +50,7 @@ class ReadLaterBubbleViewBrowserTest : public DialogBrowserTest { ...@@ -52,7 +50,7 @@ class ReadLaterBubbleViewBrowserTest : public DialogBrowserTest {
private: private:
base::test::ScopedFeatureList feature_list_; base::test::ScopedFeatureList feature_list_;
DISALLOW_COPY_AND_ASSIGN(ReadLaterBubbleViewBrowserTest); DISALLOW_COPY_AND_ASSIGN(ReadLaterButtonBrowserTest);
}; };
// TODO(1115950): Flaky on Windows. // TODO(1115950): Flaky on Windows.
...@@ -61,6 +59,6 @@ class ReadLaterBubbleViewBrowserTest : public DialogBrowserTest { ...@@ -61,6 +59,6 @@ class ReadLaterBubbleViewBrowserTest : public DialogBrowserTest {
#else #else
#define MAYBE_InvokeUi_default InvokeUi_default #define MAYBE_InvokeUi_default InvokeUi_default
#endif #endif
IN_PROC_BROWSER_TEST_F(ReadLaterBubbleViewBrowserTest, MAYBE_InvokeUi_default) { IN_PROC_BROWSER_TEST_F(ReadLaterButtonBrowserTest, MAYBE_InvokeUi_default) {
ShowAndVerifyUi(); ShowAndVerifyUi();
} }
...@@ -2187,6 +2187,7 @@ if (!is_android) { ...@@ -2187,6 +2187,7 @@ if (!is_android) {
"../browser/ui/views/autofill/payments/webauthn_dialog_browsertest.cc", "../browser/ui/views/autofill/payments/webauthn_dialog_browsertest.cc",
"../browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc", "../browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc",
"../browser/ui/views/bookmarks/bookmark_editor_view_browsertest.cc", "../browser/ui/views/bookmarks/bookmark_editor_view_browsertest.cc",
"../browser/ui/views/bubble/webui_bubble_manager_browsertest.cc",
"../browser/ui/views/certificate_selector_dialog_browsertest.cc", "../browser/ui/views/certificate_selector_dialog_browsertest.cc",
"../browser/ui/views/collected_cookies_views_browsertest.cc", "../browser/ui/views/collected_cookies_views_browsertest.cc",
"../browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc", "../browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc",
...@@ -2272,7 +2273,7 @@ if (!is_android) { ...@@ -2272,7 +2273,7 @@ if (!is_android) {
"../browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc", "../browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc",
"../browser/ui/views/profiles/profile_menu_view_browsertest.cc", "../browser/ui/views/profiles/profile_menu_view_browsertest.cc",
"../browser/ui/views/qrcode_generator/qrcode_generator_bubble_browsertest.cc", "../browser/ui/views/qrcode_generator/qrcode_generator_bubble_browsertest.cc",
"../browser/ui/views/read_later/read_later_bubble_view_browsertest.cc", "../browser/ui/views/read_later/read_later_button_browsertest.cc",
"../browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog_browsertest.cc", "../browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog_browsertest.cc",
"../browser/ui/views/select_file_dialog_extension_browsertest.cc", "../browser/ui/views/select_file_dialog_extension_browsertest.cc",
"../browser/ui/views/session_crashed_bubble_view_browsertest.cc", "../browser/ui/views/session_crashed_bubble_view_browsertest.cc",
...@@ -5950,6 +5951,8 @@ test("unit_tests") { ...@@ -5950,6 +5951,8 @@ test("unit_tests") {
"../browser/ui/views/bookmarks/bookmark_context_menu_unittest.cc", "../browser/ui/views/bookmarks/bookmark_context_menu_unittest.cc",
"../browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc", "../browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc",
"../browser/ui/views/bookmarks/bookmark_menu_delegate_unittest.cc", "../browser/ui/views/bookmarks/bookmark_menu_delegate_unittest.cc",
"../browser/ui/views/bubble/webui_bubble_dialog_view_unittest.cc",
"../browser/ui/views/bubble/webui_bubble_view_unittest.cc",
"../browser/ui/views/confirm_bubble_views_unittest.cc", "../browser/ui/views/confirm_bubble_views_unittest.cc",
"../browser/ui/views/content_setting_bubble_contents_unittest.cc", "../browser/ui/views/content_setting_bubble_contents_unittest.cc",
"../browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc", "../browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc",
......
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