Commit 52f12a40 authored by Collin Baker's avatar Collin Baker Committed by Commit Bot

Add WebUI tab strip layout logic to C++ side

Thumbnail sizes will soon be dynamic. WebUITabStripContainerView will
need to resize itself when they change, so it needs to know how the tab
strip will be layed out. This CL adds layout calculation logic in
C++-side code so both the tab strip and the WebUITabStripContainerView
can use it.

Bug: 1013646
Change-Id: If45238985e304ec42721a96518b7f1afc264f256
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1872784
Commit-Queue: Collin Baker <collinbaker@chromium.org>
Reviewed-by: default avatarJohn Lee <johntlee@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#708866}
parent 9795c672
......@@ -4036,6 +4036,8 @@ jumbo_static_library("ui") {
"views/frame/webui_tab_strip_container_view.h",
"webui/tab_strip/tab_strip_ui.cc",
"webui/tab_strip/tab_strip_ui.h",
"webui/tab_strip/tab_strip_ui_layout.cc",
"webui/tab_strip/tab_strip_ui_layout.h",
"webui/tab_strip/thumbnail_tracker.cc",
"webui/tab_strip/thumbnail_tracker.h",
]
......
......@@ -2524,12 +2524,33 @@ void BrowserView::InitViews() {
tab_strip_region_view_->AddChildView(tabstrip_); // Takes ownership.
tabstrip_controller->InitFromModel(tabstrip_);
// Create WebViews early so |webui_tab_strip_| can observe their size.
devtools_web_view_ = new views::WebView(browser_->profile());
devtools_web_view_->SetID(VIEW_ID_DEV_TOOLS_DOCKED);
devtools_web_view_->SetVisible(false);
contents_web_view_ = new ContentsWebView(browser_->profile());
contents_web_view_->SetID(VIEW_ID_TAB_CONTAINER);
contents_web_view_->SetEmbedFullscreenWidgetMode(true);
contents_container_ = new views::View();
contents_container_->AddChildView(devtools_web_view_);
contents_container_->AddChildView(contents_web_view_);
contents_container_->SetLayoutManager(std::make_unique<ContentsLayoutManager>(
devtools_web_view_, contents_web_view_));
views::View* webui_tab_strip_view = nullptr;
#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
if (base::FeatureList::IsEnabled(features::kWebUITabStrip)) {
// We use |contents_container_| here so that enabling or disabling
// devtools won't affect the tab sizes. We still use only
// |contents_web_view_| for screenshotting and will adjust the
// screenshot accordingly. Ideally, the thumbnails should be sized
// based on a typical tab size, ignoring devtools or e.g. the
// downloads bar.
webui_tab_strip_ = top_container_->AddChildView(
std::make_unique<WebUITabStripContainerView>(browser_.get()));
std::make_unique<WebUITabStripContainerView>(browser_.get(),
contents_container_));
webui_tab_strip_view = webui_tab_strip_;
}
#endif // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
......@@ -2546,22 +2567,9 @@ void BrowserView::InitViews() {
if (!toolbar_button_provider_)
SetToolbarButtonProvider(toolbar_);
contents_web_view_ = new ContentsWebView(browser_->profile());
contents_web_view_->SetID(VIEW_ID_TAB_CONTAINER);
contents_web_view_->SetEmbedFullscreenWidgetMode(true);
web_contents_close_handler_.reset(
new WebContentsCloseHandler(contents_web_view_));
devtools_web_view_ = new views::WebView(browser_->profile());
devtools_web_view_->SetID(VIEW_ID_DEV_TOOLS_DOCKED);
devtools_web_view_->SetVisible(false);
contents_container_ = new views::View();
contents_container_->AddChildView(devtools_web_view_);
contents_container_->AddChildView(contents_web_view_);
contents_container_->SetLayoutManager(std::make_unique<ContentsLayoutManager>(
devtools_web_view_, contents_web_view_));
AddChildView(contents_container_);
set_contents_view(contents_container_);
......
......@@ -16,8 +16,10 @@
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -30,10 +32,13 @@
#include "ui/views/layout/flex_layout.h"
#include "ui/views/view_class_properties.h"
WebUITabStripContainerView::WebUITabStripContainerView(Browser* browser)
WebUITabStripContainerView::WebUITabStripContainerView(
Browser* browser,
views::View* tab_contents_container)
: browser_(browser),
web_view_(
AddChildView(std::make_unique<views::WebView>(browser->profile()))) {
AddChildView(std::make_unique<views::WebView>(browser->profile()))),
tab_contents_container_(tab_contents_container) {
SetVisible(false);
// TODO(crbug.com/1010589) WebContents are initially assumed to be visible by
// default unless explicitly hidden. The WebContents need to be set to hidden
......@@ -49,7 +54,13 @@ WebUITabStripContainerView::WebUITabStripContainerView(Browser* browser)
task_manager::WebContentsTags::CreateForTabContents(
web_view_->web_contents());
TabStripUI* tab_strip_ui = static_cast<TabStripUI*>(
DCHECK(tab_contents_container);
view_observer_.Add(tab_contents_container_);
desired_height_ = TabStripUILayout::CalculateForWebViewportSize(
tab_contents_container_->size())
.CalculateContainerHeight();
TabStripUI* const tab_strip_ui = static_cast<TabStripUI*>(
web_view_->GetWebContents()->GetWebUI()->GetController());
tab_strip_ui->Initialize(browser_, this);
}
......@@ -100,9 +111,13 @@ void WebUITabStripContainerView::ShowContextMenuAtPoint(
views::MenuAnchorPosition::kTopLeft, ui::MENU_SOURCE_MOUSE);
}
TabStripUILayout WebUITabStripContainerView::GetLayout() {
return TabStripUILayout::CalculateForWebViewportSize(
tab_contents_container_->size());
}
int WebUITabStripContainerView::GetHeightForWidth(int w) const {
constexpr int kWebUITabStripHeightDp = 248;
return kWebUITabStripHeightDp;
return desired_height_;
}
void WebUITabStripContainerView::ButtonPressed(views::Button* sender,
......@@ -117,3 +132,15 @@ void WebUITabStripContainerView::ButtonPressed(views::Button* sender,
NOTREACHED();
}
}
void WebUITabStripContainerView::OnViewBoundsChanged(View* observed_view) {
DCHECK_EQ(tab_contents_container_, observed_view);
desired_height_ =
TabStripUILayout::CalculateForWebViewportSize(observed_view->size())
.CalculateContainerHeight();
InvalidateLayout();
TabStripUI* const tab_strip_ui = static_cast<TabStripUI*>(
web_view_->GetWebContents()->GetWebUI()->GetController());
tab_strip_ui->LayoutChanged();
}
......@@ -7,6 +7,7 @@
#include <memory>
#include "base/scoped_observer.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui.h"
#include "chrome/common/buildflags.h"
......@@ -30,9 +31,11 @@ class Browser;
class WebUITabStripContainerView : public TabStripUI::Embedder,
public views::View,
public views::ButtonListener {
public views::ButtonListener,
public views::ViewObserver {
public:
explicit WebUITabStripContainerView(Browser* browser);
WebUITabStripContainerView(Browser* browser,
views::View* tab_contents_container);
~WebUITabStripContainerView() override;
views::NativeViewHost* GetNativeViewHost();
......@@ -47,6 +50,7 @@ class WebUITabStripContainerView : public TabStripUI::Embedder,
void ShowContextMenuAtPoint(
gfx::Point point,
std::unique_ptr<ui::MenuModel> menu_model) override;
TabStripUILayout GetLayout() override;
// views::View:
int GetHeightForWidth(int w) const override;
......@@ -54,11 +58,19 @@ class WebUITabStripContainerView : public TabStripUI::Embedder,
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// views::ViewObserver:
void OnViewBoundsChanged(View* observed_view) override;
Browser* const browser_;
views::WebView* const web_view_;
views::View* const tab_contents_container_;
int desired_height_ = 0;
std::unique_ptr<views::MenuRunner> context_menu_runner_;
std::unique_ptr<ui::MenuModel> context_menu_model_;
ScopedObserver<views::View, views::ViewObserver> view_observer_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_FRAME_WEBUI_TAB_STRIP_CONTAINER_VIEW_H_
......@@ -29,6 +29,7 @@
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
#include "chrome/browser/ui/webui/theme_handler.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/tab_strip_resources.h"
......@@ -111,6 +112,8 @@ class WebUITabContextMenu : public ui::SimpleMenuModel::Delegate,
const int tab_index_;
};
} // namespace
class TabStripUIHandler : public content::WebUIMessageHandler,
public TabStripModelObserver {
public:
......@@ -125,6 +128,12 @@ class TabStripUIHandler : public content::WebUIMessageHandler,
browser_->tab_strip_model()->AddObserver(this);
}
void NotifyLayoutChanged() {
if (!IsJavascriptAllowed())
return;
FireWebUIListener("layout-changed");
}
// TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
......@@ -215,6 +224,9 @@ class TabStripUIHandler : public content::WebUIMessageHandler,
"showTabContextMenu",
base::Bind(&TabStripUIHandler::HandleShowTabContextMenu,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getLayout", base::Bind(&TabStripUIHandler::HandleGetLayout,
base::Unretained(this)));
}
private:
......@@ -352,6 +364,14 @@ class TabStripUIHandler : public content::WebUIMessageHandler,
std::make_unique<WebUITabContextMenu>(tab_strip_model, tab_index));
}
void HandleGetLayout(const base::ListValue* args) {
AllowJavascript();
const base::Value& callback_id = args->GetList()[0];
base::Value layout = embedder_->GetLayout().AsDictionary();
ResolveJavascriptCallback(callback_id, layout);
}
void AddTrackedTab(const base::ListValue* args) {
AllowJavascript();
......@@ -399,8 +419,6 @@ class TabStripUIHandler : public content::WebUIMessageHandler,
DISALLOW_COPY_AND_ASSIGN(TabStripUIHandler);
};
} // namespace
TabStripUI::TabStripUI(content::WebUI* web_ui)
: content::WebUIController(web_ui) {
Profile* profile = Profile::FromWebUI(web_ui);
......@@ -452,6 +470,11 @@ TabStripUI::~TabStripUI() {}
void TabStripUI::Initialize(Browser* browser, Embedder* embedder) {
content::WebUI* const web_ui = TabStripUI::web_ui();
DCHECK_EQ(Profile::FromWebUI(web_ui), browser->profile());
web_ui->AddMessageHandler(
std::make_unique<TabStripUIHandler>(browser, embedder));
auto handler = std::make_unique<TabStripUIHandler>(browser, embedder);
handler_ = handler.get();
web_ui->AddMessageHandler(std::move(handler));
}
void TabStripUI::LayoutChanged() {
handler_->NotifyLayoutChanged();
}
......@@ -12,10 +12,12 @@
#include "content/public/browser/web_ui_controller.h"
class Browser;
class TabStripUIHandler;
struct TabStripUILayout;
namespace gfx {
class Point;
}
} // namespace gfx
namespace ui {
class MenuModel;
......@@ -37,19 +39,28 @@ class TabStripUI : public content::WebUIController {
virtual void ShowContextMenuAtPoint(
gfx::Point point,
std::unique_ptr<ui::MenuModel> menu_model) = 0;
virtual TabStripUILayout GetLayout() = 0;
};
explicit TabStripUI(content::WebUI* web_ui);
~TabStripUI() override;
// Initialize TabStripUI with its embedder and the Browser it's running in.
// Must be called exactly once. The WebUI won't work until this is called.
// Initialize TabStripUI with its embedder and the Browser it's
// running in. Must be called exactly once. The WebUI won't work until
// this is called.
void Initialize(Browser* browser, Embedder* embedder);
// The embedder should call this whenever the result of
// Embedder::GetLayout() changes.
void LayoutChanged();
private:
void HandleThumbnailUpdate(int extension_tab_id,
ThumbnailTracker::CompressedThumbnailData image);
TabStripUIHandler* handler_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TabStripUI);
};
......
......@@ -14,6 +14,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
#include "chrome/common/chrome_isolated_world_ids.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
......@@ -37,6 +38,7 @@ class MockTabStripUIEmbedder : public TabStripUI::Embedder {
ShowContextMenuAtPoint,
(gfx::Point point, std::unique_ptr<ui::MenuModel> menu_model),
(override));
MOCK_METHOD(TabStripUILayout, GetLayout, (), (override));
};
} // namespace
......@@ -57,6 +59,11 @@ class TabStripUIBrowserTest : public InProcessBrowserTest {
}
void SetUpOnMainThread() override {
const TabStripUILayout default_layout =
TabStripUILayout::CalculateForWebViewportSize(gfx::Size(200, 200));
ON_CALL(mock_embedder_, GetLayout())
.WillByDefault(::testing::Return(default_layout));
webui_contents_ = content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile()));
......
// 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/webui/tab_strip/tab_strip_ui_layout.h"
#include "base/values.h"
#include "ui/gfx/geometry/size.h"
// static
TabStripUILayout TabStripUILayout::CalculateForWebViewportSize(
const gfx::Size& viewport_size) {
// The smaller of the thumbnail's height or width is fixed to this
// value. The other dimension will be at least this long.
constexpr int kThumbnailMinDimensionLength = 176;
TabStripUILayout layout;
layout.padding_around_tab_list = 16;
layout.tab_title_height = 40;
if (viewport_size.IsEmpty()) {
layout.tab_thumbnail_size =
gfx::Size(kThumbnailMinDimensionLength, kThumbnailMinDimensionLength);
return layout;
}
// Size the thumbnail to match the web viewport's aspect ratio.
if (viewport_size.width() > viewport_size.height()) {
layout.tab_thumbnail_size.set_height(kThumbnailMinDimensionLength);
layout.tab_thumbnail_size.set_width(kThumbnailMinDimensionLength *
viewport_size.width() /
viewport_size.height());
} else {
layout.tab_thumbnail_size.set_width(kThumbnailMinDimensionLength);
layout.tab_thumbnail_size.set_height(kThumbnailMinDimensionLength *
viewport_size.height() /
viewport_size.width());
}
return layout;
}
base::Value TabStripUILayout::AsDictionary() const {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetIntKey("--tabstrip-tab-list-padding", padding_around_tab_list);
dict.SetIntKey("--tabstrip-tab-title-height", tab_title_height);
dict.SetIntKey("--tabstrip-tab-thumbnail-width", tab_thumbnail_size.width());
dict.SetIntKey("--tabstrip-tab-thumbnail-height",
tab_thumbnail_size.height());
return dict;
}
int TabStripUILayout::CalculateContainerHeight() const {
return 2 * padding_around_tab_list + tab_title_height +
tab_thumbnail_size.height();
}
// 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.
#ifndef CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_UI_LAYOUT_H_
#define CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_UI_LAYOUT_H_
#include "ui/gfx/geometry/size.h"
namespace base {
class Value;
}
struct TabStripUILayout {
static TabStripUILayout CalculateForWebViewportSize(
const gfx::Size& viewport_size);
// Returns a dictionary of CSS variables.
base::Value AsDictionary() const;
// Returns the tab strip's total height. This should be used to size
// its container.
int CalculateContainerHeight() const;
int padding_around_tab_list;
int tab_title_height;
gfx::Size tab_thumbnail_size;
};
#endif // CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_UI_LAYOUT_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/webui/tab_strip/tab_strip_ui_layout.h"
#include <algorithm>
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
// The test parameter is the web content's viewport size.
class TabStripUILayoutAspectRatioTest
: public ::testing::TestWithParam<gfx::Size> {};
TEST_P(TabStripUILayoutAspectRatioTest, ThumbnailHasSameAspectRatioAsViewport) {
const gfx::Size viewport_size = GetParam();
TabStripUILayout layout =
TabStripUILayout::CalculateForWebViewportSize(viewport_size);
EXPECT_EQ(176, std::min(layout.tab_thumbnail_size.width(),
layout.tab_thumbnail_size.height()));
EXPECT_FLOAT_EQ(
viewport_size.width() / viewport_size.height(),
layout.tab_thumbnail_size.width() / layout.tab_thumbnail_size.height());
}
INSTANTIATE_TEST_SUITE_P(SmallSizes,
TabStripUILayoutAspectRatioTest,
::testing::Values(gfx::Size(200, 200),
gfx::Size(200, 300),
gfx::Size(300, 200)));
INSTANTIATE_TEST_SUITE_P(LargeSizes,
TabStripUILayoutAspectRatioTest,
::testing::Values(gfx::Size(1920, 1080),
gfx::Size(1080, 1920)));
TEST(TabStripUILayoutTest, HandlesZeroSize) {
TabStripUILayout layout =
TabStripUILayout::CalculateForWebViewportSize(gfx::Size(0, 0));
EXPECT_FALSE(layout.tab_thumbnail_size.IsEmpty());
}
......@@ -5235,7 +5235,10 @@ test("unit_tests") {
}
if (enable_webui_tab_strip) {
sources += [ "../browser/ui/webui/tab_strip/thumbnail_tracker_unittest.cc" ]
sources += [
"../browser/ui/webui/tab_strip/tab_strip_ui_layout_unittest.cc",
"../browser/ui/webui/tab_strip/thumbnail_tracker_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