chromium/chrome/browser/ui/views/frame/browser_view_ash.cc

// Copyright 2023 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_ash.h"

#include <algorithm>

#include "base/check.h"
#include "chrome/browser/ui/sad_tab_helper.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/sad_tab_view.h"
#include "chrome/browser/ui/views/side_panel/side_panel.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/controls/webview/webview.h"

BrowserViewAsh::BrowserViewAsh(std::unique_ptr<Browser> browser)
    : BrowserView(std::move(browser)) {}

void BrowserViewAsh::Layout(PassKey) {
  LayoutSuperclass<BrowserView>(this);

  // In ChromeOS ash we round the bottom two corners of the browser frame by
  // rounding the respective corners of visible client contents i.e main web
  // contents, devtools web contents and side panel. When ever there is change
  // in the layout or visibility of these contents (devtools opened, devtools
  // docked placement change, side panel open etc), we might need to update
  // which corners are currently rounded. See
  // `BrowserNonClientFrameViewChromeOS::UpdateWindowRoundedCorners()` for more
  // details.
  DCHECK(GetWidget());
  GetWidget()->non_client_view()->frame_view()->UpdateWindowRoundedCorners();
}

void BrowserViewAsh::UpdateWindowRoundedCorners(int corner_radius) {
  SidePanel* side_panel = unified_side_panel();
  const bool right_aligned_side_panel_showing =
      side_panel->GetVisible() && side_panel->IsRightAligned();
  const bool left_aligned_side_panel_showing =
      side_panel->GetVisible() && !side_panel->IsRightAligned();

  // If side panel is visible, round one of the bottom two corners of the side
  // panel based on its alignment w.r.t to web contents.
  const gfx::RoundedCornersF side_panel_radii(
      0, 0, right_aligned_side_panel_showing ? corner_radius : 0,
      left_aligned_side_panel_showing ? corner_radius : 0);

  if (side_panel_radii != side_panel->background_radii()) {
    side_panel->SetBackgroundRadii(side_panel_radii);
  }

  views::WebView* devtools_webview = devtools_web_view();
  CHECK(devtools_webview);
  CHECK(devtools_webview->holder());

  // If devtools are visible, round one of the bottom two corners of the
  // the devtools context based on the alignment of the side panel. Since
  // devtools cover the full bounds of the web contents container, if the side
  // panel is not visible, we have to round the bottom two corners of side panel
  // irrespective of its docked placement.
  const gfx::RoundedCornersF devtools_webview_radii(
      0, 0, right_aligned_side_panel_showing ? 0 : corner_radius,
      left_aligned_side_panel_showing ? 0 : corner_radius);

  if (devtools_webview_radii_ != devtools_webview_radii) {
    devtools_webview_radii_ = devtools_webview_radii;
    devtools_webview->holder()->SetCornerRadii(devtools_webview_radii_);
  }

  const DevToolsDockedPlacement devtools_placement =
      devtools_docked_placement();
  CHECK_NE(devtools_placement, DevToolsDockedPlacement::kUnknown);

  // Rounded the contents webview.
  ContentsWebView* contents_webview = contents_web_view();
  const views::View* container = contents_container();

  const bool devtools_showing =
      contents_webview->bounds() != container->GetLocalBounds();

  // With window controls overlay enabled, the web content extends over the
  // entire window height, overlapping the window's top-two rounded corners.
  // Consequently, we need to make the top two corners of the web_view
  // rounded as well.
  const bool round_content_webview_top_corner =
      IsWindowControlsOverlayEnabled();

  const gfx::RoundedCornersF contents_webview_radii(
      round_content_webview_top_corner ? corner_radius : 0,
      round_content_webview_top_corner ? corner_radius : 0,
      right_aligned_side_panel_showing ||
              (devtools_showing &&
               devtools_placement != DevToolsDockedPlacement::kLeft)
          ? 0
          : corner_radius,
      left_aligned_side_panel_showing ||
              (devtools_showing &&
               devtools_placement != DevToolsDockedPlacement::kRight)
          ? 0
          : corner_radius);

  CHECK(contents_webview);
  CHECK(contents_webview->holder());

  if (contents_webview->web_contents()) {
    // SideTabView is shown when the renderer crashes. Initially the SabTabView
    // gets the same corners as the contents webview it gets attached to but its
    // radii needs to be updated as it is unaware of the client view layout
    // changes.
    if (auto* sad_tab_helper =
            SadTabHelper::FromWebContents(contents_webview->web_contents());
        sad_tab_helper && sad_tab_helper->sad_tab()) {
      SadTabView* sad_tab_view =
          static_cast<SadTabView*>(sad_tab_helper->sad_tab());
      if (sad_tab_view->GetBackgroundRadii() != contents_webview_radii) {
        sad_tab_view->SetBackgroundRadii(contents_webview_radii);
      }
    } else {
      // We only round contents_webview, if SadTabView is not showing.
      if (contents_webview_radii_ != contents_webview_radii) {
        contents_webview_radii_ = contents_webview_radii;
        contents_webview->holder()->SetCornerRadii(contents_webview_radii);
      }
    }
  }

  if (contents_webview->background_radii() != contents_webview_radii) {
    contents_webview->SetBackgroundRadii(contents_webview_radii);
  }
}