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

Views: Add WebBubbleDialogView and MojoBubbleWebUIController classes

This CL adds a reusable bubble and corresponding WebUIController
class to be used when implementing Secondary UI using WebUI.

See the below doc for more details.

https://docs.google.com/document/d/1UtAVhVpo8Si_XlZEdt-BH_AN4egBo1XppZEhF3_bthM/edit?usp=sharing

Bug: 1099917
Change-Id: I6b16997b05dc2bf2f208ca71dd5b3cfa11db00f9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2462223
Commit-Queue: Thomas Lukaszewicz <tluk@chromium.org>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#817521}
parent 7915ff17
......@@ -5884,6 +5884,7 @@ test("unit_tests") {
"//ui/web_dialogs:test_support",
]
sources += [
"../../ui/views/controls/webview/web_bubble_dialog_view_unittest.cc",
"../../ui/views/controls/webview/web_dialog_view_unittest.cc",
"../../ui/views/controls/webview/webview_unittest.cc",
"../browser/ui/in_product_help/feature_promo_snooze_service_unittest.cc",
......
......@@ -6,6 +6,8 @@ component("webview") {
sources = [
"unhandled_keyboard_event_handler.cc",
"unhandled_keyboard_event_handler.h",
"web_bubble_dialog_view.cc",
"web_bubble_dialog_view.h",
"web_contents_set_background_color.cc",
"web_contents_set_background_color.h",
"web_dialog_view.cc",
......@@ -49,6 +51,7 @@ component("webview") {
"//ui/gfx",
"//ui/gfx/geometry",
"//ui/views",
"//ui/webui",
]
if (is_linux || is_chromeos || is_android || is_fuchsia) {
......
......@@ -6,4 +6,5 @@ include_rules = [
"+ui/content_accelerators",
"+ui/views",
"+ui/web_dialogs",
"+ui/webui",
]
// 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 "ui/views/controls/webview/web_bubble_dialog_view.h"
#include <memory>
#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/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
namespace views {
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 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);
class BubbleWebView : public WebView {
public:
BubbleWebView(content::BrowserContext* browser_context,
WebBubbleDialogView* parent)
: WebView(browser_context), parent_(parent) {
EnableSizingFromWebContents(kMinSize, kMaxSize);
}
~BubbleWebView() override = default;
// WebView:
void PreferredSizeChanged() override {
WebView::PreferredSizeChanged();
parent_->OnWebViewSizeChanged();
}
// content::WebContentsDelegate:
bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) override {
// Ignores context menu.
return true;
}
content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) override {
// 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;
}
private:
WebBubbleDialogView* parent_;
};
} // namespace
WebBubbleDialogView::WebBubbleDialogView(
content::BrowserContext* browser_context,
View* anchor_view)
: BubbleDialogDelegateView(anchor_view, BubbleBorder::TOP_RIGHT),
web_view_(AddChildView(
std::make_unique<BubbleWebView>(browser_context, this))) {
SetButtons(ui::DIALOG_BUTTON_NONE);
set_margins(gfx::Insets());
SetLayoutManager(std::make_unique<FillLayout>());
}
WebBubbleDialogView::~WebBubbleDialogView() = default;
void WebBubbleDialogView::OnWebViewSizeChanged() {
SizeToContents();
}
gfx::Size WebBubbleDialogView::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 WebBubbleDialogView::AddedToWidget() {
BubbleDialogDelegateView::AddedToWidget();
web_view_->holder()->SetCornerRadii(gfx::RoundedCornersF(GetCornerRadius()));
}
void WebBubbleDialogView::ShowUI() {
DCHECK(GetWidget());
GetWidget()->Show();
web_view_->GetWebContents()->Focus();
}
} // 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.
#ifndef UI_VIEWS_CONTROLS_WEBVIEW_WEB_BUBBLE_DIALOG_VIEW_H_
#define UI_VIEWS_CONTROLS_WEBVIEW_WEB_BUBBLE_DIALOG_VIEW_H_
#include <memory>
#include <utility>
#include "base/memory/weak_ptr.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/controls/webview/webview_export.h"
#include "ui/webui/mojo_bubble_web_ui_controller.h"
namespace content {
class BrowserContext;
} // namespace content
namespace views {
class Widget;
// A BubbleDialogDelegateView that hosts WebUI and resizes to fit the hosted
// WebContents.
class WEBVIEW_EXPORT WebBubbleDialogView
: public BubbleDialogDelegateView,
public ui::MojoBubbleWebUIController::Embedder {
public:
// |CreateWebBubbleDialog()| returns a Widget instance owned by its
// NativeWidget. When the NativeWidget is destroyed (in response to a native
// destruction message), it deletes the Widget from its destructor.
template <typename T>
static Widget* CreateWebBubbleDialog(
std::unique_ptr<WebBubbleDialogView> bubble_view,
const GURL& url) {
bubble_view->LoadURL<T>(url);
return BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
}
WebBubbleDialogView(content::BrowserContext* browser_context,
View* anchor_view);
WebBubbleDialogView(const WebBubbleDialogView&) = delete;
WebBubbleDialogView& operator=(const WebBubbleDialogView&) = delete;
~WebBubbleDialogView() override;
void OnWebViewSizeChanged();
WebView* web_view() { return web_view_; }
// BubbleDialogDelegateView:
gfx::Size CalculatePreferredSize() const override;
void AddedToWidget() override;
// MojoBubbleWebUIController::Embedder:
void ShowUI() override;
private:
// The type T enables WebBubbleDialogView to know what WebUIController is
// being used for the bubble's WebUI and allows it to make sure the associated
// WebUI is a MojoBubbleWebUIController at compile time.
template <typename T>
void LoadURL(const GURL& url) {
web_view_->LoadInitialURL(url);
T* webui_bubble_controller = web_view_->GetWebContents()
->GetWebUI()
->GetController()
->template GetAs<T>();
// Depends on the WebUIController object being constructed synchronously
// when the navigation is started in LoadInitialURL().
webui_bubble_controller->set_embedder(weak_ptr_factory_.GetWeakPtr());
}
WebView* web_view_;
base::WeakPtrFactory<WebBubbleDialogView> weak_ptr_factory_{this};
};
} // namespace views
#endif // UI_VIEWS_CONTROLS_WEBVIEW_WEB_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 "ui/views/controls/webview/web_bubble_dialog_view.h"
#include <memory>
#include <utility>
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/unique_widget_ptr.h"
namespace views {
namespace test {
class WebBubbleDialogViewTest : public ViewsTestBase {
public:
WebBubbleDialogViewTest()
: ViewsTestBase(std::unique_ptr<base::test::TaskEnvironment>(
std::make_unique<content::BrowserTaskEnvironment>())) {}
WebBubbleDialogViewTest(const WebBubbleDialogViewTest&) = delete;
WebBubbleDialogViewTest& operator=(const WebBubbleDialogViewTest&) = delete;
~WebBubbleDialogViewTest() override = default;
// ViewsTestBase:
void SetUp() override {
ViewsTestBase::SetUp();
browser_context_ = std::make_unique<content::TestBrowserContext>();
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<WebBubbleDialogView>(
browser_context_.get(), anchor_widget_->GetContentsView());
bubble_view_ = bubble_view.get();
bubble_widget_ =
BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
}
void TearDown() override {
bubble_widget_->CloseNow();
anchor_widget_.reset();
ViewsTestBase::TearDown();
}
protected:
WebBubbleDialogView* bubble_view() { return bubble_view_; }
Widget* bubble_widget() { return bubble_widget_; }
private:
std::unique_ptr<content::TestBrowserContext> browser_context_;
views::UniqueWidgetPtr anchor_widget_;
Widget* bubble_widget_ = nullptr;
WebBubbleDialogView* bubble_view_ = nullptr;
};
TEST_F(WebBubbleDialogViewTest, TestBubbleResize) {
views::WebView* const web_view = bubble_view()->web_view();
constexpr gfx::Size web_view_initial_size(100, 100);
web_view->SetPreferredSize(gfx::Size(100, 100));
bubble_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_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());
}
} // namespace test
} // namespace views
......@@ -4,6 +4,8 @@
static_library("webui") {
sources = [
"mojo_bubble_web_ui_controller.cc",
"mojo_bubble_web_ui_controller.h",
"mojo_web_ui_controller.cc",
"mojo_web_ui_controller.h",
"webui_allowlist.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 "ui/webui/mojo_bubble_web_ui_controller.h"
#include "content/public/browser/web_ui.h"
namespace ui {
MojoBubbleWebUIController::MojoBubbleWebUIController(content::WebUI* contents,
bool enable_chrome_send)
: MojoWebUIController(contents, enable_chrome_send) {}
MojoBubbleWebUIController::~MojoBubbleWebUIController() = default;
} // namespace ui
// 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 UI_WEBUI_MOJO_BUBBLE_WEB_UI_CONTROLLER_H_
#define UI_WEBUI_MOJO_BUBBLE_WEB_UI_CONTROLLER_H_
#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "ui/webui/mojo_web_ui_controller.h"
namespace content {
class WebUI;
} // namespace content
namespace ui {
class MojoBubbleWebUIController : public MojoWebUIController {
public:
class Embedder {
public:
virtual void ShowUI() = 0;
};
// By default MojoBubbleWebUIController do not have normal WebUI bindings.
// Pass |enable_chrome_send| as true if these are needed.
explicit MojoBubbleWebUIController(content::WebUI* contents,
bool enable_chrome_send = false);
MojoBubbleWebUIController(const MojoBubbleWebUIController&) = delete;
MojoBubbleWebUIController& operator=(const MojoBubbleWebUIController&) =
delete;
~MojoBubbleWebUIController() override;
void set_embedder(base::WeakPtr<Embedder> embedder) { embedder_ = embedder; }
base::WeakPtr<Embedder> embedder() { return embedder_; }
private:
base::WeakPtr<Embedder> embedder_;
};
} // namespace ui
#endif // UI_WEBUI_MOJO_BUBBLE_WEB_UI_CONTROLLER_H_
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