Commit 29fce993 authored by Elaine Chien's avatar Elaine Chien Committed by Chromium LUCI CQ

Adding initial bubble UI and basic functionalities for Chrome Labs

Bug: 1145666
Change-Id: I9bf3357d7d03437ca75d2fe6150ee771ceab5d7a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2543083Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Elaine Chien <elainechien@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832407}
parent a6352183
......@@ -7005,6 +7005,7 @@ bool SkipConditionalFeatureEntry(const flags_ui::FlagsStorage* storage,
#endif // OS_WIN
if (!strcmp("dns-over-https", entry.internal_name) &&
SystemNetworkContextManager::GetInstance() &&
(SystemNetworkContextManager::GetStubResolverConfigReader()
->ShouldDisableDohForManaged() ||
features::kDnsOverHttpsShowUiParam.Get())) {
......@@ -7083,6 +7084,10 @@ void GetFlagFeatureEntriesForDeprecatedPage(
base::BindRepeating(&ShouldSkipNonDeprecatedFeatureEntry));
}
flags_ui::FlagsState* GetCurrentFlagsState() {
return FlagsStateSingleton::GetFlagsState();
}
bool IsRestartNeededToCommitChanges() {
return FlagsStateSingleton::GetFlagsState()->IsRestartNeededToCommitChanges();
}
......
......@@ -72,6 +72,9 @@ void GetFlagFeatureEntriesForDeprecatedPage(
base::ListValue* supported_entries,
base::ListValue* unsupported_entries);
// Gets the FlagsState used in about_flags.
flags_ui::FlagsState* GetCurrentFlagsState();
// Returns true if one of the feature entry flags has been flipped since
// startup.
bool IsRestartNeededToCommitChanges();
......
......@@ -4049,6 +4049,14 @@ static_library("ui") {
"views/toolbar/browser_actions_container.h",
"views/toolbar/browser_app_menu_button.cc",
"views/toolbar/browser_app_menu_button.h",
"views/toolbar/chrome_labs_bubble_view.cc",
"views/toolbar/chrome_labs_bubble_view.h",
"views/toolbar/chrome_labs_bubble_view_model.cc",
"views/toolbar/chrome_labs_bubble_view_model.h",
"views/toolbar/chrome_labs_button.cc",
"views/toolbar/chrome_labs_button.h",
"views/toolbar/chrome_labs_item_view.cc",
"views/toolbar/chrome_labs_item_view.h",
"views/toolbar/extension_toolbar_menu_view.cc",
"views/toolbar/extension_toolbar_menu_view.h",
"views/toolbar/home_button.cc",
......
......@@ -8,6 +8,10 @@
namespace features {
// Enables Chrome Labs menu in the toolbar. See https://crbug.com/1145666
const base::Feature kChromeLabs{"ChromeLabs",
base::FEATURE_DISABLED_BY_DEFAULT};
// Enables showing the EV certificate details in the Page Info bubble.
const base::Feature kEvDetailsInPageInfo{"EvDetailsInPageInfo",
base::FEATURE_ENABLED_BY_DEFAULT};
......
......@@ -20,6 +20,8 @@ namespace features {
// All features in alphabetical order. The features should be documented
// alongside the definition of their values in the .cc file.
extern const base::Feature kChromeLabs;
extern const base::Feature kEvDetailsInPageInfo;
#if BUILDFLAG(ENABLE_EXTENSIONS)
......
// 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/toolbar/chrome_labs_bubble_view.h"
#include "base/bind.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/ui/webui/flags_ui.h"
#include "chrome/grit/generated_resources.h"
#include "components/flags_ui/pref_service_flags_storage.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
namespace {
ChromeLabsBubbleView* g_chrome_labs_bubble = nullptr;
} // namespace
// TODO(elainechien): Add screenshots and strings for translation when UI is
// finished.
class ChromeLabsFooter : public views::View {
public:
ChromeLabsFooter() {
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kVertical);
AddChildView(views::Builder<views::Label>()
.CopyAddressTo(&restart_label_)
.SetText(base::ASCIIToUTF16(
"Your changes will take effect the next time you "
"relaunch Google Chrome."))
.SetMultiLine(true)
.Build());
AddChildView(views::Builder<views::MdTextButton>()
.CopyAddressTo(&restart_button_)
.SetCallback(base::BindRepeating(&chrome::AttemptRestart))
.SetText(base::ASCIIToUTF16("Relaunch"))
.Build());
restart_label_->SizeToFit(
restart_button_->CalculatePreferredSize().width());
}
// views::View
gfx::Size CalculatePreferredSize() const override {
int width = restart_button_->CalculatePreferredSize().width();
int height = GetHeightForWidth(width);
return gfx::Size(width, height);
}
private:
views::MdTextButton* restart_button_;
views::Label* restart_label_;
};
// static
void ChromeLabsBubbleView::Show(views::View* anchor_view) {
g_chrome_labs_bubble = new ChromeLabsBubbleView(
anchor_view, std::make_unique<ChromeLabsBubbleViewModel>());
views::Widget* const widget =
BubbleDialogDelegateView::CreateBubble(g_chrome_labs_bubble);
widget->Show();
}
ChromeLabsBubbleView::ChromeLabsBubbleView(
views::View* anchor_view,
std::unique_ptr<ChromeLabsBubbleViewModel> model)
: BubbleDialogDelegateView(anchor_view,
views::BubbleBorder::Arrow::TOP_RIGHT),
model_(std::move(model)) {
SetButtons(ui::DIALOG_BUTTON_NONE);
SetShowCloseButton(true);
SetTitle(base::ASCIIToUTF16("Chrome Labs"));
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kVertical)
.SetDefault(views::kMarginsKey, gfx::Insets(5));
// TODO(elainechien): ChromeOS specific logic for creating FlagsStorage
flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
g_browser_process->local_state());
flags_state_ = about_flags::GetCurrentFlagsState();
menu_item_container_ = AddChildView(std::make_unique<views::View>());
menu_item_container_->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetDefault(views::kMarginsKey, gfx::Insets(10));
// Create each lab item.
std::vector<std::string> lab_internal_names = model_->GetLabInfo();
for (const auto& internal_name : lab_internal_names) {
const flags_ui::FeatureEntry* entry =
flags_state_->FindFeatureEntryByName(internal_name);
DCHECK_EQ(entry->type, flags_ui::FeatureEntry::FEATURE_VALUE);
int default_index = GetIndexOfEnabledLabState(entry);
menu_item_container_->AddChildView(
CreateLabItem(internal_name, default_index, entry));
}
// TODO(elainechien): Build UI for 0 experiments case.
DCHECK(menu_item_container_->children().size() >= 1);
restart_prompt_ = AddChildView(std::make_unique<ChromeLabsFooter>());
restart_prompt_->SetVisible(about_flags::IsRestartNeededToCommitChanges());
}
std::unique_ptr<ChromeLabsItemView> ChromeLabsBubbleView::CreateLabItem(
std::string internal_name,
int default_index,
const flags_ui::FeatureEntry* entry) {
auto combobox_callback = [](ChromeLabsBubbleView* bubble_view,
std::string internal_name,
ChromeLabsItemView* item_view) {
int selected_index = item_view->GetSelectedIndex();
about_flags::SetFeatureEntryEnabled(
bubble_view->flags_storage_.get(),
internal_name + "@" + base::NumberToString(selected_index), true);
bubble_view->ShowRelaunchPrompt();
};
return std::make_unique<ChromeLabsItemView>(
internal_name, default_index, entry,
base::BindRepeating(combobox_callback, this, internal_name));
}
int ChromeLabsBubbleView::GetIndexOfEnabledLabState(
const flags_ui::FeatureEntry* entry) {
std::set<std::string> enabled_entries;
flags_state_->GetSanitizedEnabledFlags(flags_storage_.get(),
&enabled_entries);
for (int i = 0; i < entry->NumOptions(); i++) {
const std::string name = entry->NameForOption(i);
if (enabled_entries.count(name) > 0)
return i;
}
return 0;
}
void ChromeLabsBubbleView::ShowRelaunchPrompt() {
restart_prompt_->SetVisible(about_flags::IsRestartNeededToCommitChanges());
DCHECK_EQ(g_chrome_labs_bubble, this);
g_chrome_labs_bubble->SizeToContents();
}
ChromeLabsBubbleView::~ChromeLabsBubbleView() {
g_chrome_labs_bubble = nullptr;
}
// static
bool ChromeLabsBubbleView::IsShowing() {
return g_chrome_labs_bubble != nullptr &&
g_chrome_labs_bubble->GetWidget() != nullptr;
}
// static
void ChromeLabsBubbleView::Hide() {
if (IsShowing())
g_chrome_labs_bubble->GetWidget()->Close();
}
// static
ChromeLabsBubbleView*
ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting() {
return g_chrome_labs_bubble;
}
flags_ui::FlagsState* ChromeLabsBubbleView::GetFlagsStateForTesting() {
return flags_state_;
}
flags_ui::FlagsStorage* ChromeLabsBubbleView::GetFlagsStorageForTesting() {
return flags_storage_.get();
}
views::View* ChromeLabsBubbleView::GetMenuItemContainerForTesting() {
return menu_item_container_;
}
bool ChromeLabsBubbleView::IsRestartPromptVisibleForTesting() {
return restart_prompt_->GetVisible();
}
// 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_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_H_
#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
#include "chrome/browser/ui/views/toolbar/chrome_labs_item_view.h"
#include "components/flags_ui/feature_entry.h"
#include "components/flags_ui/flags_state.h"
#include "components/flags_ui/flags_storage.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
// TODO(elainechien): Use composition instead of inheritance.
class ChromeLabsBubbleView : public views::BubbleDialogDelegateView {
public:
static void Show(views::View* anchor_view);
static bool IsShowing();
static void Hide();
~ChromeLabsBubbleView() override;
// Getter functions for testing.
static ChromeLabsBubbleView* GetChromeLabsBubbleViewForTesting();
flags_ui::FlagsState* GetFlagsStateForTesting();
flags_ui::FlagsStorage* GetFlagsStorageForTesting();
views::View* GetMenuItemContainerForTesting();
bool IsRestartPromptVisibleForTesting();
private:
ChromeLabsBubbleView(views::View* anchor_view,
std::unique_ptr<ChromeLabsBubbleViewModel> model);
std::unique_ptr<ChromeLabsItemView> CreateLabItem(
std::string internal_name,
int default_index,
const flags_ui::FeatureEntry* entry);
int GetIndexOfEnabledLabState(const flags_ui::FeatureEntry* entry);
void ShowRelaunchPrompt();
std::unique_ptr<flags_ui::FlagsStorage> flags_storage_;
flags_ui::FlagsState* flags_state_;
// This view will hold all the child lab items.
views::View* menu_item_container_;
std::unique_ptr<ChromeLabsBubbleViewModel> model_;
views::View* restart_prompt_;
};
#endif // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_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/toolbar/chrome_labs_bubble_view_model.h"
#include "base/strings/utf_string_conversions.h"
ChromeLabsBubbleViewModel::ChromeLabsBubbleViewModel() {
SetUpLabs();
}
std::vector<std::string> ChromeLabsBubbleViewModel::GetLabInfo() const {
return lab_internal_names_;
}
// TODO(elainechien): Explore better ways to allow developers to add their
// experiments.
// Experiments featured in labs must have feature entries of type FEATURE_VALUE
// (Default, Enabled, Disabled states). Experiments with multiple parameters may
// be considered in the future.
void ChromeLabsBubbleViewModel::SetUpLabs() {
lab_internal_names_.push_back("read-later");
}
ChromeLabsBubbleViewModel::~ChromeLabsBubbleViewModel() = 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_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_MODEL_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_MODEL_H_
#include <vector>
#include "base/strings/string16.h"
class ChromeLabsBubbleViewModel {
public:
ChromeLabsBubbleViewModel();
~ChromeLabsBubbleViewModel();
std::vector<std::string> GetLabInfo() const;
private:
void SetUpLabs();
std::vector<std::string> lab_internal_names_;
};
#endif // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_MODEL_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/toolbar/chrome_labs_bubble_view.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
#include "chrome/browser/ui/views/toolbar/chrome_labs_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "components/flags_ui/flags_state.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/test/combobox_test_api.h"
#include "ui/views/test/widget_test.h"
class ChromeLabsBubbleTest : public TestWithBrowserView {
public:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kChromeLabs);
TestWithBrowserView::SetUp();
ChromeLabsButton* button = chrome_labs_button();
ChromeLabsBubbleView::Show(button);
}
void TearDown() override {
chrome_labs_bubble()->GetFlagsStateForTesting()->Reset();
TestWithBrowserView::TearDown();
}
ChromeLabsBubbleView* chrome_labs_bubble() {
return ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting();
}
ChromeLabsButton* chrome_labs_button() {
return browser_view()->toolbar()->chrome_labs_button();
}
views::View* chrome_labs_menu_item_container() {
return ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting()
->GetMenuItemContainerForTesting();
}
flags_ui::FlagsState* flags_state() {
return ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting()
->GetFlagsStateForTesting();
}
flags_ui::FlagsStorage* flags_storage() {
return ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting()
->GetFlagsStorageForTesting();
}
ChromeLabsItemView* first_lab_item() {
views::View* menu_items = chrome_labs_menu_item_container();
return static_cast<ChromeLabsItemView*>(menu_items->children().front());
}
// Returns true if the option at index |option_index| is the enabled feature
// state.
bool IsSelected(int option_index, const flags_ui::FeatureEntry* entry) {
std::string internal_name = std::string(entry->internal_name) + "@" +
base::NumberToString(option_index);
std::set<std::string> enabled_entries;
flags_state()->GetSanitizedEnabledFlags(flags_storage(), &enabled_entries);
for (int i = 0; i < entry->NumOptions(); i++) {
const std::string name = entry->NameForOption(i);
if (internal_name == name && enabled_entries.count(name) > 0) {
return true;
}
}
return false;
}
// Returns true if none of the entry's options have been enabled.
bool IsDefault(const flags_ui::FeatureEntry* entry) {
std::set<std::string> enabled_entries;
flags_state()->GetSanitizedEnabledFlags(flags_storage(), &enabled_entries);
for (int i = 0; i < entry->NumOptions(); i++) {
const std::string name = entry->NameForOption(i);
if (enabled_entries.count(name) > 0) {
return false;
}
}
return true;
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
class ChromeLabsFeatureTest : public ChromeLabsBubbleTest,
public testing::WithParamInterface<int> {
public:
ChromeLabsFeatureTest() = default;
};
// This test checks that selecting an option through the combobox on a lab will
// enable the corresponding option on the feature.
TEST_P(ChromeLabsFeatureTest, ChangeSelectedOption) {
int row = GetParam();
ChromeLabsItemView* lab_item = first_lab_item();
views::Combobox* lab_item_combobox =
lab_item->GetLabStateComboboxForTesting();
lab_item_combobox->SetSelectedRow(row);
const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
EXPECT_TRUE(IsSelected(row, feature_entry));
}
// For FeatureEntries of type FEATURE_VALUE, the option at index 1 corresponds
// to "Enabled" and the option at index 2 corresponds to "Disabled".
INSTANTIATE_TEST_SUITE_P(All, ChromeLabsFeatureTest, testing::Values(1, 2));
// This test checks that selecting row 0 will reset the feature to it's Default
// state.
TEST_F(ChromeLabsBubbleTest, ResetToDefault) {
ChromeLabsItemView* lab_item = first_lab_item();
views::Combobox* lab_item_combobox =
lab_item->GetLabStateComboboxForTesting();
// Selects an option and then attempts to reset the lab to Default by
// selecting 0.
const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
lab_item_combobox->SetSelectedRow(1);
EXPECT_FALSE(IsDefault(feature_entry));
lab_item_combobox->SetSelectedRow(0);
EXPECT_TRUE(IsDefault(feature_entry));
}
// This test checks that the restart prompt becomes visible when a lab state is
// changed.
TEST_F(ChromeLabsBubbleTest, RestartPromptShows) {
ChromeLabsBubbleView* bubble_view = chrome_labs_bubble();
ChromeLabsItemView* lab_item = first_lab_item();
views::Combobox* lab_item_combobox =
lab_item->GetLabStateComboboxForTesting();
EXPECT_FALSE(bubble_view->IsRestartPromptVisibleForTesting());
lab_item_combobox->SetSelectedRow(1);
EXPECT_TRUE(bubble_view->IsRestartPromptVisibleForTesting());
views::test::WidgetDestroyedWaiter destroyed_waiter(bubble_view->GetWidget());
ChromeLabsBubbleView::Hide();
destroyed_waiter.Wait();
ChromeLabsBubbleView::Show(chrome_labs_button());
ChromeLabsBubbleView* bubble_view_after_restart = chrome_labs_bubble();
EXPECT_TRUE(bubble_view_after_restart->IsRestartPromptVisibleForTesting());
}
// This test checks that the restart prompt does not show when the lab state has
// not changed.
// TODO(elainechien): This currently only works for default. This will be
// changed to work for all states. See design doc in crbug/1145666.
TEST_F(ChromeLabsBubbleTest, SelectDefaultTwiceNoRestart) {
ChromeLabsBubbleView* bubble_view = chrome_labs_bubble();
ChromeLabsItemView* lab_item = first_lab_item();
views::Combobox* lab_item_combobox =
lab_item->GetLabStateComboboxForTesting();
// Select default state when the originally instantiated state was already
// default.
lab_item_combobox->SetSelectedRow(0);
EXPECT_FALSE(bubble_view->IsRestartPromptVisibleForTesting());
}
// 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/toolbar/chrome_labs_button.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
#include "ui/views/controls/button/button_controller.h"
ChromeLabsButton::ChromeLabsButton()
: ToolbarButton(base::BindRepeating(&ChromeLabsButton::ButtonPressed,
base::Unretained(this))) {
button_controller()->set_notify_action(
views::ButtonController::NotifyAction::kOnPress);
}
void ChromeLabsButton::UpdateIcon() {
// TODO(elainechien): Replace kHorizontalMenuIcon with Chrome Labs icon when
// available.
UpdateIconsWithStandardColors(kHorizontalMenuIcon);
}
const char* ChromeLabsButton::GetClassName() const {
return "ChromeLabsButton";
}
void ChromeLabsButton::ButtonPressed() {
if (ChromeLabsBubbleView::IsShowing()) {
ChromeLabsBubbleView::Hide();
return;
}
ChromeLabsBubbleView::Show(this);
}
// 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_TOOLBAR_CHROME_LABS_BUTTON_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUTTON_H_
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
class ChromeLabsButton : public ToolbarButton {
public:
ChromeLabsButton();
// ToolbarButton:
void UpdateIcon() override;
// views::View:
const char* GetClassName() const override;
private:
void ButtonPressed();
};
#endif // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUTTON_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/toolbar/chrome_labs_button.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "ui/events/event_utils.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/test/widget_test.h"
class ChromeLabsButtonTest : public TestWithBrowserView {
public:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kChromeLabs);
TestWithBrowserView::SetUp();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(ChromeLabsButtonTest, ShowAndHideChromeLabsBubbleOnPress) {
ChromeLabsButton* labs_button =
browser_view()->toolbar()->chrome_labs_button();
EXPECT_FALSE(ChromeLabsBubbleView::IsShowing());
ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
views::test::ButtonTestApi test_api(labs_button);
test_api.NotifyClick(e);
EXPECT_TRUE(ChromeLabsBubbleView::IsShowing());
views::test::WidgetDestroyedWaiter destroyed_waiter(
ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting()->GetWidget());
test_api.NotifyClick(e);
destroyed_waiter.Wait();
EXPECT_FALSE(ChromeLabsBubbleView::IsShowing());
}
// 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/toolbar/chrome_labs_item_view.h"
#include "ui/base/models/combobox_model.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
class LabsComboboxModel : public ui::ComboboxModel {
public:
explicit LabsComboboxModel(const flags_ui::FeatureEntry* feature_entry,
int default_index)
: feature_entry_(feature_entry), default_index_(default_index) {}
// ui::ComboboxModel:
int GetItemCount() const override { return feature_entry_->NumOptions(); }
base::string16 GetItemAt(int index) const override {
// TODO(elainechien): remove white space for description
return feature_entry_->DescriptionForOption(index);
}
int GetDefaultIndex() const override { return default_index_; }
private:
const flags_ui::FeatureEntry* feature_entry_;
int default_index_;
};
ChromeLabsItemView::ChromeLabsItemView(
std::string internal_name,
int default_index,
const flags_ui::FeatureEntry* feature_entry,
base::RepeatingCallback<void(ChromeLabsItemView* item_view)>
combobox_callback)
: internal_name_(internal_name), feature_entry_(feature_entry) {
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetDefault(views::kMarginsKey, gfx::Insets(10));
AddChildView(views::Builder<views::Label>()
.SetText(base::ASCIIToUTF16(feature_entry->visible_name))
.Build());
AddChildView(views::Builder<views::Combobox>()
.CopyAddressTo(&lab_state_combobox_)
.SetOwnedModel(std::make_unique<LabsComboboxModel>(
feature_entry_, default_index))
.SetCallback(base::BindRepeating(combobox_callback, this))
.Build());
}
int ChromeLabsItemView::GetSelectedIndex() {
return lab_state_combobox_->GetSelectedIndex();
}
const flags_ui::FeatureEntry* ChromeLabsItemView::GetFeatureEntry() {
return feature_entry_;
}
// 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_TOOLBAR_CHROME_LABS_ITEM_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_ITEM_VIEW_H_
#include "base/callback.h"
#include "components/flags_ui/feature_entry.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/view.h"
class ChromeLabsItemView : public views::View {
public:
ChromeLabsItemView(
std::string internal_name,
int default_index,
const flags_ui::FeatureEntry* feature_entry,
base::RepeatingCallback<void(ChromeLabsItemView* item_view)>
combobox_callback);
int GetSelectedIndex();
views::Combobox* GetLabStateComboboxForTesting() {
return lab_state_combobox_;
}
const flags_ui::FeatureEntry* GetFeatureEntry();
private:
// Combobox with selected state of the lab.
views::Combobox* lab_state_combobox_;
std::string internal_name_;
const flags_ui::FeatureEntry* feature_entry_;
};
#endif // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_ITEM_VIEW_H_
......@@ -52,6 +52,7 @@
#include "chrome/browser/ui/views/toolbar/back_forward_button.h"
#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
#include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
#include "chrome/browser/ui/views/toolbar/chrome_labs_button.h"
#include "chrome/browser/ui/views/toolbar/home_button.h"
#include "chrome/browser/ui/views/toolbar/reload_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.h"
......@@ -270,6 +271,10 @@ void ToolbarView::Init() {
if (extensions_container)
extensions_container_ = AddChildView(std::move(extensions_container));
if (base::FeatureList::IsEnabled(features::kChromeLabs)) {
chrome_labs_button_ = AddChildView(std::make_unique<ChromeLabsButton>());
}
if (cast)
cast_ = AddChildView(std::move(cast));
......
......@@ -46,6 +46,7 @@ class BrowserAppMenuButton;
class Browser;
class ExtensionsToolbarButton;
class ExtensionsToolbarContainer;
class ChromeLabsButton;
class HomeButton;
class MediaToolbarButtonView;
class ReloadButton;
......@@ -135,6 +136,7 @@ class ToolbarView : public views::AccessiblePaneView,
// Accessors.
Browser* browser() const { return browser_; }
BrowserActionsContainer* browser_actions() const { return browser_actions_; }
ChromeLabsButton* chrome_labs_button() const { return chrome_labs_button_; }
ExtensionsToolbarContainer* extensions_container() const {
return extensions_container_;
}
......@@ -267,6 +269,7 @@ class ToolbarView : public views::AccessiblePaneView,
LocationBarView* location_bar_ = nullptr;
BrowserActionsContainer* browser_actions_ = nullptr;
ExtensionsToolbarContainer* extensions_container_ = nullptr;
ChromeLabsButton* chrome_labs_button_ = nullptr;
media_router::CastToolbarButton* cast_ = nullptr;
ToolbarAccountIconContainerView* toolbar_account_icon_container_ = nullptr;
AvatarToolbarButton* avatar_ = nullptr;
......
......@@ -6072,6 +6072,8 @@ test("unit_tests") {
"../browser/ui/views/tabs/tab_strip_layout_unittest.cc",
"../browser/ui/views/tabs/tab_strip_unittest.cc",
"../browser/ui/views/tabs/tab_unittest.cc",
"../browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc",
"../browser/ui/views/toolbar/chrome_labs_button_unittest.cc",
"../browser/ui/views/toolbar/reload_button_unittest.cc",
"../browser/ui/views/toolbar/toolbar_action_view_unittest.cc",
"../browser/ui/views/toolbar/toolbar_actions_bar_bubble_views_unittest.cc",
......
......@@ -96,6 +96,16 @@ class FlagsState {
const char* enable_features_flag_name,
const char* disable_features_flag_name);
// Returns the FeatureEntry named |internal_name|. Returns null if no entry is
// matched.
const FeatureEntry* FindFeatureEntryByName(
const std::string& internal_name) const;
// Gets sanitized entries from |flags_storage|, filtering out any entries that
// don't exist in |feature_entries_|, and updates |flags_storage|.
void GetSanitizedEnabledFlags(FlagsStorage* flags_storage,
std::set<std::string>* result) const;
// Reads the state from |flags_storage| and fills |switches| with the set of
// switches corresponding to enabled entries and |features| with the set of
// strings corresponding to enabled/disabled base::Feature states. Feature
......@@ -214,10 +224,6 @@ class FlagsState {
const std::set<std::string>& enabled_entries,
int platform_mask) const;
// Gets sanitized entries from |flags_storage|, filtering out any entries that
// don't exist in |feature_entries_|, and updates |flags_storage|.
void GetSanitizedEnabledFlags(FlagsStorage* flags_storage,
std::set<std::string>* result) const;
// Variant of GetSanitizedEnabledFlags that also removes any flags that aren't
// enabled on the current platform.
......@@ -234,10 +240,6 @@ class FlagsState {
std::set<std::string>* enabled_entries,
std::map<std::string, SwitchEntry>* name_to_switch_map) const;
// Returns the FeatureEntry named |internal_name|. Returns null if no entry is
// matched.
const FeatureEntry* FindFeatureEntryByName(
const std::string& internal_name) const;
// Returns whether there is a FeatureEntry named by |name| in
// |feature_entries_| that:
......
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