chromium/ash/curtain/security_curtain_widget_controller.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/curtain/security_curtain_widget_controller.h"

#include <memory>
#include <vector>

#include "base/scoped_observation.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/window_observer.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_type.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"

namespace ash::curtain {

namespace {

std::vector<std::unique_ptr<ui::Layer>> InitWidgetLayers(
    ui::Layer& root_layer) {
  // In rare cases the compositor might fail to allocate the textures.
  // To prevent the widget from being transparent in this case, we add a
  // solid color layer.
  auto solid_color_layer = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
  solid_color_layer->SetColor(SK_ColorLTGRAY);
  root_layer.Add(solid_color_layer.get());

  auto textured_layer = std::make_unique<ui::Layer>(ui::LAYER_TEXTURED);
  root_layer.Add(textured_layer.get());
  root_layer.StackAtTop(textured_layer.get());

  std::vector<std::unique_ptr<ui::Layer>> layers;
  layers.push_back(std::move(solid_color_layer));
  layers.push_back(std::move(textured_layer));
  return layers;
}

views::Widget::InitParams GetWidgetInitParams(aura::Window* parent) {
  views::Widget::InitParams result(
      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  result.name = "CurtainOverlayWidget";
  result.opacity = views::Widget::InitParams::WindowOpacity::kOpaque;
  result.parent = parent;
  // The curtain screen should not consume any events, but instead the windows
  // below should continue to receive all mouse/keyboard/... events.
  result.accept_events = false;
  // No need to set `show_state` as the window bounds are managed by the parent.
  return result;
}

std::unique_ptr<views::Widget> CreateWidget(
    aura::Window* parent,
    std::unique_ptr<views::View> content_view) {
  auto widget = std::make_unique<views::Widget>();
  widget->Init(GetWidgetInitParams(parent));
  widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE);
  widget->SetContentsView(std::move(content_view));
  return widget;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
//  WidgetMaximizer
////////////////////////////////////////////////////////////////////////////////

// Helper class that ensures the curtain widget is always maximized,
// even when the display is resized.
class SecurityCurtainWidgetController::WidgetMaximizer
    : public aura::WindowObserver {
 public:
  explicit WidgetMaximizer(views::Widget* widget) : widget_(*widget) {
    // Observe resizes
    root_window_observation_.Observe(&root_window());

    // Set initial layer dimensions
    OnRootWindowResized();
  }
  WidgetMaximizer(const WidgetMaximizer&) = delete;
  WidgetMaximizer& operator=(const WidgetMaximizer&) = delete;
  ~WidgetMaximizer() override = default;

 private:
  // aura::WindowObserver implementation:
  void OnWindowBoundsChanged(aura::Window* root,
                             const gfx::Rect& old_bounds,
                             const gfx::Rect& new_bounds,
                             ui::PropertyChangeReason reason) override {
    OnRootWindowResized();
  }

  void OnRootWindowResized() {
    gfx::Rect new_bounds(root_window().layer()->bounds().size());
    widget_->SetBounds(new_bounds);
  }

  aura::Window& root_window() {
    auto* result = widget_->GetNativeWindow()->GetRootWindow();
    DCHECK(result);
    return *result;
  }

  // We should track when the root window is resized to ensure our widget
  // remains full screen.
  base::ScopedObservation<aura::Window, aura::WindowObserver>
      root_window_observation_{this};

  raw_ref<views::Widget> widget_;
};

////////////////////////////////////////////////////////////////////////////////
//  SecurityCurtainWidgetController
////////////////////////////////////////////////////////////////////////////////

SecurityCurtainWidgetController::SecurityCurtainWidgetController(
    SecurityCurtainWidgetController&&) = default;
SecurityCurtainWidgetController& SecurityCurtainWidgetController::operator=(
    SecurityCurtainWidgetController&&) = default;
SecurityCurtainWidgetController::~SecurityCurtainWidgetController() = default;

SecurityCurtainWidgetController::SecurityCurtainWidgetController(
    std::unique_ptr<views::Widget> widget,
    Layers layers)
    : widget_layers_(std::move(layers)),
      widget_(std::move(widget)),
      occlusion_tracker_exclude_(
          std::make_unique<aura::WindowOcclusionTracker::ScopedExclude>(
              widget_->GetNativeView())),
      widget_maximizer_(std::make_unique<WidgetMaximizer>(widget_.get())) {
  DCHECK(widget_);
  widget_->Show();
}

// static
SecurityCurtainWidgetController
SecurityCurtainWidgetController::CreateForRootWindow(
    aura::Window* root_window,
    std::unique_ptr<views::View> curtain_view) {
  auto widget = CreateWidget(root_window, std::move(curtain_view));
  auto layers = InitWidgetLayers(*widget->GetLayer());
  return SecurityCurtainWidgetController(std::move(widget), std::move(layers));
}

const views::Widget& SecurityCurtainWidgetController::GetWidget() const {
  DCHECK(widget_);
  return *widget_;
}

views::Widget& SecurityCurtainWidgetController::GetWidget() {
  DCHECK(widget_);
  return *widget_;
}

}  // namespace ash::curtain