Commit bf5e1fb4 authored by Taylor Bergquist's avatar Taylor Bergquist Committed by Commit Bot

Revert "Tab strip ideal bounds are calculated from the available width instead...

Revert "Tab strip ideal bounds are calculated from the available width instead of the current width."

This reverts commit d091784a.

Reason for revert: caused crash regression https://bugs.chromium.org/p/chromium/issues/detail?id=1070980#c1

Original change's description:
> Tab strip ideal bounds are calculated from the available width instead of the current width.
> 
> This obviates the need for the work described in
> go/chrome-nonpredictive-tabstrip-animations.
> 
> One known issue: with stacked tabs enabled, resize a window to trigger
> a transition into the stacked layout. The tabs will animate into the
> new layout, and while this is taking place, they will ignore any
> further changes to the tabstrip's bounds.
> 
> Further work: Continue removing now-dead code from the new animation
> system.
> 
> Bug: 958173
> Change-Id: I977b3836ad87647fa17e431a1215ebd1ce81727c
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2055544
> Reviewed-by: Scott Violet <sky@chromium.org>
> Reviewed-by: Connie Wan <connily@chromium.org>
> Commit-Queue: Taylor Bergquist <tbergquist@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#759054}

TBR=sky@chromium.org,pbos@chromium.org,tbergquist@chromium.org,connily@chromium.org

Change-Id: I4b995aeb6515aa675442e4cf69a4a4d0be48e840
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 958173
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2151425Reviewed-by: default avatarTaylor Bergquist <tbergquist@chromium.org>
Commit-Queue: Taylor Bergquist <tbergquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759374}
parent 788a3bf1
......@@ -52,7 +52,6 @@
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/gfx/animation/animation_test_api.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_SESSION_SERVICE)
......@@ -61,10 +60,7 @@
class TabRestoreTest : public InProcessBrowserTest {
public:
TabRestoreTest()
: active_browser_list_(nullptr),
animation_mode_reset_(gfx::AnimationTestApi::SetRichAnimationRenderMode(
gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED)) {
TabRestoreTest() : active_browser_list_(NULL) {
url1_ = ui_test_utils::GetTestUrl(
base::FilePath().AppendASCII("session_history"),
base::FilePath().AppendASCII("bot1.html"));
......@@ -192,9 +188,6 @@ class TabRestoreTest : public InProcessBrowserTest {
const BrowserList* active_browser_list_;
private:
std::unique_ptr<base::AutoReset<gfx::Animation::RichAnimationRenderMode>>
animation_mode_reset_;
DISALLOW_COPY_AND_ASSIGN(TabRestoreTest);
};
......@@ -421,12 +414,12 @@ IN_PROC_BROWSER_TEST_F(TabRestoreTest, RestoreWindowBounds) {
// Deliberately change the bounds of the first window to something different.
gfx::Rect bounds = browser()->window()->GetBounds();
bounds.set_width(700);
bounds.set_width(640);
bounds.set_height(480);
bounds.Offset(20, 20);
browser()->window()->SetBounds(bounds);
gfx::Rect bounds2 = browser()->window()->GetBounds();
ASSERT_EQ(bounds, bounds2);
EXPECT_EQ(bounds, bounds2);
// Close the first window.
CloseBrowserSynchronously(browser());
......
......@@ -18,8 +18,6 @@ class TabStripRegionView final : public views::View {
const char* GetClassName() const override;
void ChildPreferredSizeChanged(views::View* child) override;
// TODO(958173): Override OnBoundsChanged to cancel tabstrip animations.
private:
DISALLOW_COPY_AND_ASSIGN(TabStripRegionView);
};
......
......@@ -19,11 +19,13 @@ constexpr base::TimeDelta kZeroDuration = base::TimeDelta::FromMilliseconds(0);
constexpr base::TimeDelta TabAnimation::kAnimationDuration;
TabAnimation::TabAnimation(TabAnimationState static_state)
TabAnimation::TabAnimation(TabAnimationState static_state,
base::OnceClosure tab_removed_callback)
: initial_state_(static_state),
target_state_(static_state),
start_time_(base::TimeTicks::Now()),
duration_(kZeroDuration) {}
duration_(kZeroDuration),
tab_removed_callback_(std::move(tab_removed_callback)) {}
TabAnimation::~TabAnimation() = default;
......@@ -57,6 +59,10 @@ void TabAnimation::CompleteAnimation() {
duration_ = kZeroDuration;
}
void TabAnimation::NotifyCloseCompleted() {
std::move(tab_removed_callback_).Run();
}
base::TimeDelta TabAnimation::GetTimeRemaining() const {
return std::max(start_time_ + duration_ - base::TimeTicks::Now(),
kZeroDuration);
......
......@@ -20,7 +20,8 @@ class TabAnimation {
base::TimeDelta::FromMilliseconds(200);
// Creates a TabAnimation for a tab with no active animations.
explicit TabAnimation(TabAnimationState static_state);
TabAnimation(TabAnimationState static_state,
base::OnceClosure tab_removed_callback);
~TabAnimation();
......@@ -40,6 +41,10 @@ class TabAnimation {
void CompleteAnimation();
// Notifies the owner of the animated tab that the close animation
// has completed and the tab can be cleaned up.
void NotifyCloseCompleted();
TabAnimationState target_state() const { return target_state_; }
base::TimeDelta GetTimeRemaining() const;
......@@ -58,6 +63,7 @@ class TabAnimation {
TabAnimationState target_state_;
base::TimeTicks start_time_;
base::TimeDelta duration_;
base::OnceClosure tab_removed_callback_;
DISALLOW_COPY_AND_ASSIGN(TabAnimation);
};
......
// 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/tabs/tab_animation.h"
#include <utility>
#include "base/bind.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chrome/browser/ui/tabs/tab_types.h"
#include "chrome/browser/ui/views/tabs/tab_animation_state.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr base::TimeDelta kZeroDuration = base::TimeDelta::FromMilliseconds(0);
} // namespace
class TabAnimationTest : public testing::Test {
public:
TabAnimationTest()
: env_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
float CurrentPinnedness(const TabAnimation& animation) {
return animation.GetCurrentState().pinnedness();
}
base::test::TaskEnvironment env_;
};
TEST_F(TabAnimationTest, StaticAnimationDoesNotChange) {
TabAnimationState static_state = TabAnimationState::ForIdealTabState(
TabOpen::kOpen, TabPinned::kUnpinned, TabActive::kInactive, 0);
TabAnimation static_animation(static_state, base::BindOnce([]() {}));
EXPECT_EQ(kZeroDuration, static_animation.GetTimeRemaining());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(0),
static_animation.GetTimeRemaining());
EXPECT_EQ(static_state.pinnedness(), CurrentPinnedness(static_animation));
env_.FastForwardBy(TabAnimation::kAnimationDuration);
EXPECT_EQ(static_state.pinnedness(), CurrentPinnedness(static_animation));
}
TEST_F(TabAnimationTest, AnimationAnimates) {
TabAnimationState initial_state = TabAnimationState::ForIdealTabState(
TabOpen::kOpen, TabPinned::kUnpinned, TabActive::kInactive, 0);
TabAnimationState target_state = initial_state.WithPinned(TabPinned::kPinned);
TabAnimation animation(initial_state, base::BindOnce([]() {}));
animation.AnimateTo(target_state);
EXPECT_LT(kZeroDuration, animation.GetTimeRemaining());
EXPECT_EQ(initial_state.pinnedness(), CurrentPinnedness(animation));
env_.FastForwardBy(TabAnimation::kAnimationDuration / 2.0);
EXPECT_LT(kZeroDuration, animation.GetTimeRemaining());
EXPECT_LT(initial_state.pinnedness(), CurrentPinnedness(animation));
EXPECT_LT(CurrentPinnedness(animation), target_state.pinnedness());
env_.FastForwardBy(TabAnimation::kAnimationDuration / 2.0);
EXPECT_EQ(target_state.pinnedness(), CurrentPinnedness(animation));
}
TEST_F(TabAnimationTest, CompletedAnimationSnapsToTarget) {
TabAnimationState initial_state = TabAnimationState::ForIdealTabState(
TabOpen::kOpen, TabPinned::kUnpinned, TabActive::kInactive, 0);
TabAnimationState target_state = initial_state.WithPinned(TabPinned::kPinned);
TabAnimation animation(initial_state, base::BindOnce([]() {}));
animation.AnimateTo(target_state);
animation.CompleteAnimation();
EXPECT_EQ(kZeroDuration, animation.GetTimeRemaining());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(0), animation.GetTimeRemaining());
EXPECT_EQ(target_state.pinnedness(), CurrentPinnedness(animation));
}
TEST_F(TabAnimationTest, ReplacedAnimationRestartsDuration) {
TabAnimationState initial_state = TabAnimationState::ForIdealTabState(
TabOpen::kOpen, TabPinned::kUnpinned, TabActive::kInactive, 0);
TabAnimationState target_state = initial_state.WithPinned(TabPinned::kPinned);
TabAnimation animation(initial_state, base::BindOnce([]() {}));
animation.AnimateTo(target_state);
env_.FastForwardBy(TabAnimation::kAnimationDuration / 2.0);
TabAnimationState reversal_state = animation.GetCurrentState();
animation.AnimateTo(initial_state);
EXPECT_EQ(reversal_state.pinnedness(), CurrentPinnedness(animation));
EXPECT_EQ(TabAnimation::kAnimationDuration, animation.GetTimeRemaining());
}
TEST_F(TabAnimationTest, RetargetedAnimationKeepsDuration) {
TabAnimationState initial_state = TabAnimationState::ForIdealTabState(
TabOpen::kOpen, TabPinned::kUnpinned, TabActive::kInactive, 0);
TabAnimationState target_state = initial_state.WithPinned(TabPinned::kPinned);
TabAnimation animation(initial_state, base::BindOnce([]() {}));
animation.AnimateTo(target_state);
env_.FastForwardBy(TabAnimation::kAnimationDuration / 2.0);
EXPECT_EQ(TabAnimation::kAnimationDuration / 2.0,
animation.GetTimeRemaining());
animation.RetargetTo(initial_state);
EXPECT_EQ(TabAnimation::kAnimationDuration / 2.0,
animation.GetTimeRemaining());
env_.FastForwardBy(TabAnimation::kAnimationDuration);
EXPECT_EQ(initial_state.pinnedness(), CurrentPinnedness(animation));
}
TEST_F(TabAnimationTest, TestNotifyCloseCompleted) {
class TabClosedDetector {
public:
void NotifyTabClosed() { was_closed_ = true; }
bool was_closed_ = false;
};
TabAnimationState static_state = TabAnimationState::ForIdealTabState(
TabOpen::kOpen, TabPinned::kUnpinned, TabActive::kInactive, 0);
TabClosedDetector tab_closed_detector;
TabAnimation animation(
static_state, base::BindOnce(&TabClosedDetector::NotifyTabClosed,
base::Unretained(&tab_closed_detector)));
EXPECT_FALSE(tab_closed_detector.was_closed_);
animation.NotifyCloseCompleted();
EXPECT_TRUE(tab_closed_detector.was_closed_);
}
This diff is collapsed.
......@@ -184,7 +184,8 @@ class TabStrip : public views::AccessiblePaneView,
// Needed to ensure display and focus order of the group header view.
void OnGroupMoved(const tab_groups::TabGroupId& group);
// Destroys the views associated with a recently deleted tab group.
// Destroys the views associated with a recently deleted tab group. The
// associated view mappings are erased in OnGroupCloseAnimationCompleted().
void OnGroupClosed(const tab_groups::TabGroupId& group);
// Attempts to move the specified group to the left.
......@@ -210,6 +211,7 @@ class TabStrip : public views::AccessiblePaneView,
void SetTabNeedsAttention(int model_index, bool attention);
// Retrieves the ideal bounds for the Tab at the specified index.
// TODO(958173): The notion of ideal bounds is going away. Delete this.
const gfx::Rect& ideal_bounds(int tab_data_index) const {
return tabs_.ideal_bounds(tab_data_index);
}
......@@ -324,7 +326,6 @@ class TabStrip : public views::AccessiblePaneView,
void Layout() override;
void PaintChildren(const views::PaintInfo& paint_info) override;
const char* GetClassName() const override;
gfx::Size GetMinimumSize() const override;
gfx::Size CalculatePreferredSize() const override;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
......@@ -401,6 +402,17 @@ class TabStrip : public views::AccessiblePaneView,
DISALLOW_COPY_AND_ASSIGN(DropArrow);
};
// Specifies how to handle tabs that are midway through closing when falling
// back from |layout_helper_| to |bounds_animator_|.
enum class ClosingTabsBehavior {
// Keep the tabs alive, because responsibilities for destroying them lie
// with |bounds_animator_|.
kTransferOwnership,
// Destroy the tabs, because responsibilities for destroying them lie with
// |layout_helper_|.
kDestroy
};
void Init();
views::ViewModelT<Tab>* tabs_view_model() { return &tabs_; }
......@@ -416,6 +428,8 @@ class TabStrip : public views::AccessiblePaneView,
// Animates the removal of the tab at |model_index| using the old animation
// style.
// TODO(958173): Delete this once all animations have been migrated to the
// new animation style.
void StartFallbackRemoveTabAnimation(int model_index, bool was_active);
// Invoked from |MoveTab| after |tab_data_| has been updated to animate the
......@@ -425,7 +439,9 @@ class TabStrip : public views::AccessiblePaneView,
// Animates all the views to their ideal bounds.
// NOTE: this does *not* invoke UpdateIdealBounds, it uses the bounds
// currently set in ideal_bounds.
void AnimateToIdealBounds();
// TODO(958173): The notion of ideal bounds is going away. Delete this.
void AnimateToIdealBounds(ClosingTabsBehavior closing_tabs_behavior =
ClosingTabsBehavior::kDestroy);
void ExitTabClosingMode();
......@@ -467,10 +483,6 @@ class TabStrip : public views::AccessiblePaneView,
// button.
int GetTabAreaWidth() const;
// Returns the width of the area right of the tabs reserved for the new tab
// button and the frame grab area.
int GetRightSideReservedWidth() const;
// Returns the X coordinate the new tab button should be placed at. Requires
// |tabs_| to have correct ideal bounds.
int GetNewTabButtonIdealX() const;
......@@ -493,6 +505,10 @@ class TabStrip : public views::AccessiblePaneView,
// code and is not a general-purpose method.
void OnTabCloseAnimationCompleted(Tab* tab);
// Cleans up the TabGroupHeader for |group| from the TabStrip. This is called
// from the tab animation code and is not a general-purpose method.
void OnGroupCloseAnimationCompleted(const tab_groups::TabGroupId& group);
// Invoked from StoppedDraggingTabs to cleanup |view|. If |view| is known
// |is_first_view| is set to true.
void StoppedDraggingView(TabSlotView* view, bool* is_first_view);
......@@ -551,18 +567,16 @@ class TabStrip : public views::AccessiblePaneView,
void PrepareForAnimation();
// Generates and sets the ideal bounds for each of the tabs as well as the new
// tab button. Note: Does not animate the tabs to those bounds so callers can
// use this information for other purposes - see AnimateToIdealBounds.
// tab button.
// TODO(958173): The notion of ideal bounds is going away. Delete this.
void UpdateIdealBounds();
// Generates and sets the ideal bounds for the pinned tabs. Returns the index
// to position the first non-pinned tab and sets |first_non_pinned_index| to
// the index of the first non-pinned tab.
// TODO(958173): The notion of ideal bounds is going away. Delete this.
int UpdateIdealBoundsForPinnedTabs(int* first_non_pinned_index);
// Calculates the width that can be occupied by the tabs in the strip.
int CalculateAvailableWidthForTabs();
// Starts various types of TabStrip animations.
void StartResizeLayoutAnimation();
void StartPinnedTabAnimation();
......@@ -605,9 +619,6 @@ class TabStrip : public views::AccessiblePaneView,
// whenever any input of the computation of the border's sizing changes.
void UpdateNewTabButtonBorder();
// Called whenever a tab animation has progressed.
void OnTabAnimationProgressed();
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
......@@ -663,6 +674,8 @@ class TabStrip : public views::AccessiblePaneView,
std::unique_ptr<TabStripLayoutHelper> layout_helper_;
// Responsible for animating tabs in response to model changes.
// Deprecated; https://crbug.com/958173 tracks migrating animations from
// |bounds_animator_| to |TabStripLayoutHelper::animator_|.
views::BoundsAnimator bounds_animator_{this};
// The "New Tab" button.
......
......@@ -38,13 +38,17 @@ class TabStripLayoutHelper {
TabStripLayoutHelper(const TabStripController* controller,
GetTabsCallback get_tabs_callback,
GetGroupHeadersCallback get_group_headers_callback);
GetGroupHeadersCallback get_group_headers_callback,
base::RepeatingClosure on_animation_progressed);
~TabStripLayoutHelper();
// Returns a vector of all tabs in the strip, including both closing tabs
// and tabs still in the model.
std::vector<Tab*> GetTabs();
// Returns whether any animations for tabs or group headers are in progress.
bool IsAnimating() const;
int active_tab_width() { return active_tab_width_; }
int inactive_tab_width() { return inactive_tab_width_; }
int first_non_pinned_tab_index() { return first_non_pinned_tab_index_; }
......@@ -59,12 +63,28 @@ class TabStripLayoutHelper {
return group_header_ideal_bounds_;
}
// Inserts a new tab at |index|.
void InsertTabAt(int model_index, Tab* tab, TabPinned pinned);
// Marks the tab at |model_index| as closing, but does not remove it from
// |slots_|.
void RemoveTabAt(int model_index, Tab* tab);
// Inserts a new tab at |index|, without animation. |tab_removed_callback|
// will be invoked if the tab is removed at the end of a remove animation.
void InsertTabAtNoAnimation(int model_index,
Tab* tab,
base::OnceClosure tab_removed_callback,
TabPinned pinned);
// Inserts a new tab at |index|, with animation. |tab_removed_callback| will
// be invoked if the tab is removed at the end of a remove animation.
void InsertTabAt(int model_index,
Tab* tab,
base::OnceClosure tab_removed_callback,
TabPinned pinned);
// Marks the tab at |model_index| as closed without animating it. Use when
// the tab has been removed from the model but the old animation style owns
// animating it.
// TODO(958173): Remove this when the old animation style is removed.
void RemoveTabNoAnimation(int model_index, Tab* tab);
// Marks the tab at |model_index| as closing and animates it closed.
void RemoveTab(int model_index, Tab* tab);
// Called when the tabstrip enters tab closing mode, wherein tabs should
// resize differently to control which tab ends up under the cursor.
......@@ -91,8 +111,11 @@ class TabStripLayoutHelper {
// Sets the tab at |index|'s pinned state to |pinned|.
void SetTabPinned(int model_index, TabPinned pinned);
// Inserts a new group header for |group|.
void InsertGroupHeader(tab_groups::TabGroupId group, TabGroupHeader* header);
// Inserts a new group header for |group|. |header_removed_callback| will be
// invoked if the group is removed at the end of a remove animation.
void InsertGroupHeader(tab_groups::TabGroupId group,
TabGroupHeader* header,
base::OnceClosure header_removed_callback);
// Removes the group header for |group|.
void RemoveGroupHeader(tab_groups::TabGroupId group);
......@@ -105,21 +128,26 @@ class TabStripLayoutHelper {
// Changes the active tab from |prev_active_index| to |new_active_index|.
void SetActiveTab(int prev_active_index, int new_active_index);
// Calculates the smallest width the tabs can occupy.
int CalculateMinimumWidth();
// Finishes all in-progress animations.
void CompleteAnimations();
// Calculates the width the tabs would occupy if they have enough space.
int CalculatePreferredWidth();
// TODO(958173): Temporary method that completes running animations
// without invoking the callback to destroy removed tabs. Use to hand
// off animation (and removed tab destruction) responsibilities from
// this animator to elsewhere without teleporting tabs or destroying
// the same tab more than once.
void CompleteAnimationsWithoutDestroyingTabs();
// Generates and sets the ideal bounds for the views in |tabs| and
// |group_headers|. Updates the cached widths in |active_tab_width_| and
// |inactive_tab_width_|. Returns the total width occupied by the new ideal
// bounds.
int UpdateIdealBounds(int available_width);
// |inactive_tab_width_|.
// TODO(958173): The notion of ideal bounds is going away. Delete this.
void UpdateIdealBounds(int available_width);
// Generates and sets the ideal bounds for |tabs|. Updates
// the cached values in |first_non_pinned_tab_index_| and
// |first_non_pinned_tab_x_|.
// TODO(958173): The notion of ideal bounds is going away. Delete this.
void UpdateIdealBoundsForPinnedTabs();
// Lays out tabs and group headers to their current bounds. Returns the
......@@ -129,11 +157,6 @@ class TabStripLayoutHelper {
private:
struct TabSlot;
// Calculates the bounds each tab should occupy, subject to the provided
// width constraint.
std::vector<gfx::Rect> CalculateIdealBounds(
base::Optional<int> available_width);
// Given a tab's |model_index| and |group|, returns the index of its
// corresponding TabSlot in |slots_|.
int GetSlotIndexForTabModelIndex(
......@@ -147,6 +170,15 @@ class TabStripLayoutHelper {
// Returns the current width constraints for each View.
std::vector<TabWidthConstraints> GetCurrentTabWidthConstraints() const;
// Runs an animation for the View at |slot_index| towards |target_state|.
void AnimateSlot(int slot_index, TabAnimationState target_state);
// Called when animations progress.
void TickAnimations();
// Deletes the data in |slots_| corresponding to fully closed tabs.
void RemoveClosedTabs();
// Recalculate |cached_slots_|, called whenever state changes.
void UpdateCachedTabSlots();
......@@ -173,6 +205,12 @@ class TabStripLayoutHelper {
GetTabsCallback get_tabs_callback_;
GetGroupHeadersCallback get_group_headers_callback_;
// Timer used to run animations on Views..
base::RepeatingTimer animation_timer_;
// Called when animations progress.
base::RepeatingClosure on_animation_progressed_;
// Current collation of tabs and group headers, along with necessary data to
// run layout and animations for those Views.
std::vector<TabSlot> slots_;
......
......@@ -34,8 +34,6 @@
#include "ui/gfx/favicon_size.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"
using views::Widget;
......@@ -355,16 +353,8 @@ class AlertIndicatorTest : public ChromeViewsTestBase {
controller_ = new FakeBaseTabStripController;
tab_strip_ = new TabStrip(std::unique_ptr<TabStripController>(controller_));
controller_->set_tab_strip(tab_strip_);
// The tab strip must be added to the view hierarchy for it to create the
// buttons.
views::FlexLayout* layout_manager =
parent_.SetLayoutManager(std::make_unique<views::FlexLayout>());
layout_manager->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetDefault(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded));
parent_.AddChildView(tab_strip_);
parent_.set_owned_by_client();
......
......@@ -5369,6 +5369,7 @@ test("unit_tests") {
"../browser/ui/views/tabs/fake_base_tab_strip_controller.cc",
"../browser/ui/views/tabs/fake_base_tab_strip_controller.h",
"../browser/ui/views/tabs/stacked_tab_strip_layout_unittest.cc",
"../browser/ui/views/tabs/tab_animation_unittest.cc",
"../browser/ui/views/tabs/tab_strip_layout_unittest.cc",
"../browser/ui/views/tabs/tab_strip_unittest.cc",
"../browser/ui/views/tabs/tab_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