File: browser_view_layout_unittest.cc

package info (click to toggle)
chromium 139.0.7258.138-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,120,676 kB
  • sloc: cpp: 35,100,869; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (434 lines) | stat: -rw-r--r-- 18,171 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
// Copyright 2013 The Chromium Authors
// 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/frame/browser_view_layout.h"

#include <memory>

#include "base/containers/fixed_flat_set.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/browser_view_layout_delegate.h"
#include "chrome/browser/ui/views/frame/contents_layout_manager.h"
#include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
#include "chrome/browser/ui/views/infobars/infobar_container_view.h"
#include "chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/controls/separator.h"
#include "ui/views/test/views_test_utils.h"

namespace {

// Save space for the separator.
constexpr int kToolbarHeight = 30 - views::Separator::kThickness;
constexpr int kNewTabFooterHeight = 56;
constexpr int kBaseWidth = 800;

class MockBrowserViewLayoutDelegate : public BrowserViewLayoutDelegate {
 public:
  MockBrowserViewLayoutDelegate() = default;

  MockBrowserViewLayoutDelegate(const MockBrowserViewLayoutDelegate&) = delete;
  MockBrowserViewLayoutDelegate& operator=(
      const MockBrowserViewLayoutDelegate&) = delete;

  ~MockBrowserViewLayoutDelegate() override = default;

  void set_should_draw_tab_strip(bool visible) {
    should_draw_tab_strip_ = visible;
  }
  void set_toolbar_visible(bool visible) { toolbar_visible_ = visible; }
  void set_bookmark_bar_visible(bool visible) {
    bookmark_bar_visible_ = visible;
  }
  void set_content_separator_enabled(bool visible) {
    content_separator_enabled_ = visible;
  }
  void set_top_controls_slide_enabled(bool enabled) {
    top_controls_slide_enabled_ = enabled;
  }
  void set_top_controls_shown_ratio(float ratio) {
    top_controls_shown_ratio_ = ratio;
  }

  // BrowserViewLayout::Delegate overrides:
  bool ShouldDrawTabStrip() const override { return should_draw_tab_strip_; }
  bool GetBorderlessModeEnabled() const override { return false; }
  gfx::Rect GetBoundsForTabStripRegionInBrowserView() const override {
    return gfx::Rect();
  }
  gfx::Rect GetBoundsForWebAppFrameToolbarInBrowserView() const override {
    return gfx::Rect();
  }
  int GetTopInsetInBrowserView() const override { return 0; }
  bool IsToolbarVisible() const override { return toolbar_visible_; }
  bool IsBookmarkBarVisible() const override { return bookmark_bar_visible_; }
  bool IsContentsSeparatorEnabled() const override {
    return content_separator_enabled_;
  }
  bool IsActiveTabSplit() const override { return false; }
  ExclusiveAccessBubbleViews* GetExclusiveAccessBubble() const override {
    return nullptr;
  }
  bool IsTopControlsSlideBehaviorEnabled() const override {
    return top_controls_slide_enabled_;
  }
  float GetTopControlsSlideBehaviorShownRatio() const override {
    return top_controls_shown_ratio_;
  }
  bool SupportsWindowFeature(
      const Browser::WindowFeature feature) const override {
    static constexpr auto kSupportedFeatures =
        base::MakeFixedFlatSet<Browser::WindowFeature>({
            Browser::FEATURE_TABSTRIP,
            Browser::FEATURE_TOOLBAR,
            Browser::FEATURE_LOCATIONBAR,
            Browser::FEATURE_BOOKMARKBAR,
        });
    return kSupportedFeatures.contains(feature);
  }
  gfx::NativeView GetHostViewForAnchoring() const override {
    return gfx::NativeView();
  }
  bool HasFindBarController() const override { return false; }
  void MoveWindowForFindBarIfNecessary() const override {}
  bool IsWindowControlsOverlayEnabled() const override { return false; }
  void UpdateWindowControlsOverlay(const gfx::Rect& rect) override {}
  bool ShouldLayoutTabStrip() const override { return true; }
  int GetExtraInfobarOffset() const override { return 0; }

 private:
  bool should_draw_tab_strip_ = true;
  bool toolbar_visible_ = true;
  bool bookmark_bar_visible_ = true;
  bool content_separator_enabled_ = true;
  bool top_controls_slide_enabled_ = false;
  float top_controls_shown_ratio_ = 1.f;
};

///////////////////////////////////////////////////////////////////////////////

std::unique_ptr<views::View> CreateFixedSizeView(const gfx::Size& size) {
  auto view = std::make_unique<views::View>();
  view->SetPreferredSize(size);
  view->SizeToPreferredSize();
  return view;
}

///////////////////////////////////////////////////////////////////////////////

class MockImmersiveModeController : public ImmersiveModeController {
 public:
  // ImmersiveModeController overrides:
  void Init(BrowserView* browser_view) override {}
  void SetEnabled(bool enabled) override {}
  bool IsEnabled() const override { return false; }
  bool ShouldHideTopViews() const override { return false; }
  bool IsRevealed() const override { return false; }
  int GetTopContainerVerticalOffset(
      const gfx::Size& top_container_size) const override {
    return 0;
  }
  std::unique_ptr<ImmersiveRevealedLock> GetRevealedLock(
      AnimateReveal animate_reveal) override {
    return nullptr;
  }
  void OnFindBarVisibleBoundsChanged(
      const gfx::Rect& new_visible_bounds) override {}
  bool ShouldStayImmersiveAfterExitingFullscreen() override { return true; }
  void OnWidgetActivationChanged(views::Widget* widget, bool active) override {}
  int GetMinimumContentOffset() const override { return 0; }
  int GetExtraInfobarOffset() const override { return 0; }
  void OnContentFullscreenChanged(bool is_content_fullscreen) override {}
};

}  // anonymous namespace

///////////////////////////////////////////////////////////////////////////////
// Tests of BrowserViewLayout. Runs tests without constructing a BrowserView.
class BrowserViewLayoutTest : public ChromeViewsTestBase {
 public:
  BrowserViewLayoutTest()
      : delegate_(nullptr),
        top_container_(nullptr),
        tab_strip_(nullptr),
        toolbar_(nullptr),
        infobar_container_(nullptr),
        contents_container_(nullptr),
        contents_web_view_(nullptr),
        devtools_web_view_(nullptr),
        new_tab_footer_web_view_(nullptr) {}

  BrowserViewLayoutTest(const BrowserViewLayoutTest&) = delete;
  BrowserViewLayoutTest& operator=(const BrowserViewLayoutTest&) = delete;

  ~BrowserViewLayoutTest() override = default;

  BrowserViewLayout* layout() { return layout_; }
  MockBrowserViewLayoutDelegate* delegate() { return delegate_; }
  views::View* browser_view() { return browser_view_.get(); }
  views::View* top_container() { return top_container_; }
  TabStrip* tab_strip() { return tab_strip_; }
  views::View* webui_tab_strip() { return webui_tab_strip_; }
  views::View* toolbar() { return toolbar_; }
  views::View* separator() { return separator_; }
  InfoBarContainerView* infobar_container() { return infobar_container_; }
  views::View* contents_container() { return contents_container_; }

  void SetUp() override {
    ChromeViewsTestBase::SetUp();

    browser_view_ = CreateFixedSizeView(gfx::Size(kBaseWidth, 600));

    immersive_mode_controller_ =
        std::make_unique<MockImmersiveModeController>();

    top_container_ = browser_view_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 60)));
    auto tab_strip = std::make_unique<TabStrip>(
        std::make_unique<FakeBaseTabStripController>());
    tab_strip_ = tab_strip.get();
    TabStripRegionView* tab_strip_region_view = top_container_->AddChildView(
        std::make_unique<TabStripRegionView>(std::move(tab_strip)));
    webui_tab_strip_ = top_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 200)));
    webui_tab_strip_->SetVisible(false);
    toolbar_ = top_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, kToolbarHeight)));
    separator_ =
        top_container_->AddChildView(std::make_unique<views::Separator>());

    infobar_container_ = browser_view_->AddChildView(
        std::make_unique<InfoBarContainerView>(nullptr));

    side_panel_rounded_corner_ =
        browser_view_->AddChildView(CreateFixedSizeView(gfx::Size(16, 16)));

    contents_container_ = browser_view_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 600)));
    devtools_web_view_ = contents_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 600)));
    devtools_web_view_->SetVisible(false);
    devtools_scrim_view_ = contents_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 600)));
    devtools_scrim_view_->SetVisible(false);
    new_tab_footer_web_view_ = contents_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, kNewTabFooterHeight)));
    new_tab_footer_web_view_->SetVisible(false);
    contents_web_view_ = contents_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 600)));
    contents_scrim_view_ = contents_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 600)));
    lens_overlay_view_ = contents_container_->AddChildView(
        CreateFixedSizeView(gfx::Size(kBaseWidth, 600)));
    contents_container_->SetLayoutManager(
        std::make_unique<ContentsLayoutManager>(
            devtools_web_view_, devtools_scrim_view_, contents_web_view_,
            lens_overlay_view_, contents_scrim_view_,
            /*contents_border_view=*/nullptr, /*watermark_view=*/nullptr,
            new_tab_footer_web_view_));

    auto delegate = std::make_unique<MockBrowserViewLayoutDelegate>();
    delegate_ = delegate.get();
    auto layout = std::make_unique<BrowserViewLayout>(
        std::move(delegate),
        /*browser_view=*/nullptr, /*window_scrim=*/nullptr, top_container_,
        /*web_app_frame_toolbar=*/nullptr,
        /*web_app_window_title=*/nullptr, tab_strip_region_view, tab_strip_,
        toolbar_, infobar_container_, contents_container_,
        /*multi_contents_view=*/nullptr,
        /*left_aligned_side_panel_separator=*/nullptr,
        /*unified_side_panel=*/nullptr,
        /*right_aligned_side_panel_separator=*/nullptr,
        side_panel_rounded_corner_, immersive_mode_controller_.get(),
        separator_);
    layout->set_webui_tab_strip(webui_tab_strip());
    layout_ = layout.get();
    browser_view_->SetLayoutManager(std::move(layout));
  }

  void TearDown() override {
    // The layout owns the delegate, so we destroy the layout and null out both
    // pointers to avoid dangling pointers.
    delegate_ = nullptr;
    layout_ = nullptr;
    browser_view_->SetLayoutManager(nullptr);

    // The other views are children of |browser_view_| and will be destroyed
    // along with it after TearDown(). Null out the pointers to avoid them
    // dangling.
    top_container_ = nullptr;
    tab_strip_ = nullptr;
    webui_tab_strip_ = nullptr;
    toolbar_ = nullptr;
    separator_ = nullptr;
    infobar_container_ = nullptr;
    side_panel_rounded_corner_ = nullptr;
    contents_container_ = nullptr;
    contents_web_view_ = nullptr;
    devtools_web_view_ = nullptr;
    devtools_scrim_view_ = nullptr;
    contents_scrim_view_ = nullptr;
    lens_overlay_view_ = nullptr;
    new_tab_footer_web_view_ = nullptr;
    ChromeViewsTestBase::TearDown();
  }

  // For the purposes of this test, boolean values are directly set on a
  // BrowserViewLayoutDelegate which are checked during layout or child view
  // visibility is directly changed. These calls do not schedule a layout and we
  // need to manually invalidate layout.
  void InvalidateAndRunScheduledLayoutOnBrowserView() {
    browser_view()->InvalidateLayout();
    views::test::RunScheduledLayout(browser_view());
  }

 private:
  raw_ptr<BrowserViewLayout> layout_;
  raw_ptr<MockBrowserViewLayoutDelegate> delegate_;  // Owned by |layout_|.
  std::unique_ptr<views::View> browser_view_;

  // Views owned by |browser_view_|.
  raw_ptr<views::View> top_container_;
  raw_ptr<TabStrip> tab_strip_;
  raw_ptr<views::View> webui_tab_strip_;
  raw_ptr<views::View> toolbar_;
  raw_ptr<views::Separator> separator_;
  raw_ptr<InfoBarContainerView> infobar_container_;
  raw_ptr<views::View> side_panel_rounded_corner_;
  raw_ptr<views::View> contents_container_;
  raw_ptr<views::View> contents_web_view_;
  raw_ptr<views::View> devtools_web_view_;
  raw_ptr<views::View> devtools_scrim_view_;
  raw_ptr<views::View> contents_scrim_view_;
  raw_ptr<views::View> lens_overlay_view_;
  raw_ptr<views::View> new_tab_footer_web_view_;

  std::unique_ptr<MockImmersiveModeController> immersive_mode_controller_;
};

// Test basic construction and initialization.
TEST_F(BrowserViewLayoutTest, BrowserViewLayout) {
  EXPECT_TRUE(layout()->GetWebContentsModalDialogHost());
  EXPECT_FALSE(layout()->IsInfobarVisible());
}

// Test the core layout functions.
TEST_F(BrowserViewLayoutTest, Layout) {
  // Simulate a window with no interesting UI.
  delegate()->set_should_draw_tab_strip(false);
  delegate()->set_toolbar_visible(false);
  delegate()->set_bookmark_bar_visible(false);
  InvalidateAndRunScheduledLayoutOnBrowserView();

  // Top views are zero-height.
  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), tab_strip()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 0), toolbar()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), infobar_container()->bounds());
  // Contents split fills the window.
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 600), contents_container()->bounds());

  // Turn on the toolbar, like in a pop-up window.
  delegate()->set_toolbar_visible(true);
  InvalidateAndRunScheduledLayoutOnBrowserView();

  // Now the toolbar has bounds and other views shift down.
  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), tab_strip()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds());
  EXPECT_EQ(
      gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness),
      separator()->bounds());
  EXPECT_EQ(gfx::Rect(0, 30, 0, 0), infobar_container()->bounds());
  EXPECT_EQ(gfx::Rect(0, 30, kBaseWidth, 570), contents_container()->bounds());

  // Disable the contents separator.
  delegate()->set_content_separator_enabled(false);
  InvalidateAndRunScheduledLayoutOnBrowserView();

  // Now the separator is not visible and the content grows vertically.
  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), tab_strip()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds());
  EXPECT_FALSE(separator()->GetVisible());
  EXPECT_EQ(gfx::Rect(0, 29, 0, 0), infobar_container()->bounds());
  EXPECT_EQ(gfx::Rect(0, 29, kBaseWidth, 571), contents_container()->bounds());

  // TODO(jamescook): Tab strip and bookmark bar.
}

TEST_F(BrowserViewLayoutTest, LayoutDownloadShelf) {
  constexpr int kHeight = 50;
  std::unique_ptr<views::View> download_shelf =
      CreateFixedSizeView(gfx::Size(kBaseWidth, kHeight));
  layout()->set_download_shelf(download_shelf.get());

  // If the download shelf isn't visible, it doesn't move the bottom edge.
  download_shelf->SetVisible(false);
  constexpr int kBottom = 500;
  EXPECT_EQ(kBottom, layout()->LayoutDownloadShelf(kBottom));

  // A visible download shelf moves the bottom edge up.
  download_shelf->SetVisible(true);
  constexpr int kTop = kBottom - kHeight;
  EXPECT_EQ(kTop, layout()->LayoutDownloadShelf(kBottom));
  EXPECT_EQ(gfx::Rect(0, kTop, 0, kHeight), download_shelf->bounds());

  // avoid dangling pointer.
  layout()->set_download_shelf(nullptr);
}

TEST_F(BrowserViewLayoutTest, LayoutContentsWithTopControlsSlideBehavior) {
  // Top controls are fully shown.
  delegate()->set_should_draw_tab_strip(false);
  delegate()->set_toolbar_visible(true);
  delegate()->set_top_controls_slide_enabled(true);
  delegate()->set_top_controls_shown_ratio(1.f);
  InvalidateAndRunScheduledLayoutOnBrowserView();
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 30), top_container()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds());
  EXPECT_EQ(
      gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness),
      separator()->bounds());
  EXPECT_EQ(gfx::Rect(0, 30, kBaseWidth, 570), contents_container()->bounds());

  // Top controls are half shown, half hidden.
  delegate()->set_top_controls_shown_ratio(0.5f);
  InvalidateAndRunScheduledLayoutOnBrowserView();
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 30), top_container()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds());
  EXPECT_EQ(
      gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness),
      separator()->bounds());
  EXPECT_EQ(gfx::Rect(0, 30, kBaseWidth, 570), contents_container()->bounds());

  // Top controls are fully hidden. the contents are expanded in height by an
  // amount equal to the top controls height.
  delegate()->set_top_controls_shown_ratio(0.f);
  InvalidateAndRunScheduledLayoutOnBrowserView();
  EXPECT_EQ(gfx::Rect(0, -30, kBaseWidth, 30), top_container()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds());
  EXPECT_EQ(
      gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness),
      separator()->bounds());
  EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 600), contents_container()->bounds());
}

TEST_F(BrowserViewLayoutTest, WebUITabStripPushesDownContents) {
  delegate()->set_should_draw_tab_strip(false);
  delegate()->set_toolbar_visible(true);
  webui_tab_strip()->SetVisible(false);
  InvalidateAndRunScheduledLayoutOnBrowserView();
  const gfx::Rect original_contents_bounds = contents_container()->bounds();
  EXPECT_EQ(gfx::Size(), webui_tab_strip()->size());

  webui_tab_strip()->SetVisible(true);
  InvalidateAndRunScheduledLayoutOnBrowserView();
  EXPECT_LT(0, webui_tab_strip()->size().height());
  EXPECT_EQ(original_contents_bounds.size(), contents_container()->size());
  EXPECT_EQ(webui_tab_strip()->size().height(),
            contents_container()->bounds().y() - original_contents_bounds.y());
}