0

Introduce security curtain screen, compliant with UX requirements

This replaces placeholder UI that was previously added
(but never released to any customers).

UX requirement: http://shortn/_tWLEP1bbdp
Implementation:
   Light mode: https://screenshot.googleplex.com/7UwiiWHr3HEVNzU.png
   Dark mode: https://screenshot.googleplex.com/BFWm4YsNSpwmkvN.png

Note that the implementation is not 100% according to the UX
requirements because to fully comply I have to wait until the
OOBE code is migrated to their new look-and-feel, which is
only scheduled for M116.
I opened b/271099991 to ensure we do not forget to update the UX at
that time.

Low-Coverage-Reason: Will add browser pixel tests to ensure the curtain looks correct, but support for that is not there yet.

Bug: b/266550061
Change-Id: I1a30de86140b7d796a55399e7573b8260d8560bc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4254387
Reviewed-by: Demetrios Papadopoulos <dpapad@chromium.org>
Reviewed-by: Renato Silva <rrsilva@google.com>
Commit-Queue: Jeroen Dhollander <jeroendh@google.com>
Cr-Commit-Position: refs/heads/main@{#1120420}
This commit is contained in:
Jeroen Dhollander
2023-03-22 09:28:02 +00:00
committed by Chromium LUCI CQ
parent 2f32cad07c
commit b93b120dca
23 changed files with 317 additions and 185 deletions

@@ -6443,14 +6443,6 @@ New install
Please unlock to view notifications Please unlock to view notifications
</message> </message>
<!-- security curtain -->
<message name="IDS_ASH_CURTAIN_TITLE" desc="Title text displayed to inform the user a remote admin has taken control of the ChromeOs device.">
Your administrator is controlling your device
</message>
<message name="IDS_ASH_CURTAIN_DESCRIPTION" desc="Detailed description displayed to inform the user a remote admin has taken control of the ChromeOs device.">
Your administrator has logged in to look into an issue. You can continue to use the device after the administrator gives the control back to you.
</message>
<!-- pagination view --> <!-- pagination view -->
<message name="IDS_ASH_PAGINATION_LEFT_ARROW_TOOLTIP" desc="The tooltip text used for pagination left arrow button." > <message name="IDS_ASH_PAGINATION_LEFT_ARROW_TOOLTIP" desc="The tooltip text used for pagination left arrow button." >
Previous page Previous page

@@ -1 +0,0 @@
1a2410a9c35ae9f30af8833c20f46f7490f09839

@@ -1 +0,0 @@
1a2410a9c35ae9f30af8833c20f46f7490f09839

@@ -6,195 +6,75 @@
#include <memory> #include <memory>
#include "ash/public/cpp/shelf_config.h" #include "ash/assistant/ui/base/stack_layout.h"
#include "ash/public/cpp/style/color_provider.h" #include "ash/public/cpp/ash_web_view.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/public/cpp/ash_web_view_factory.h"
#include "ash/strings/grit/ash_strings.h" #include "ash/wallpaper/wallpaper_constants.h"
#include "ash/wallpaper/wallpaper_view.h"
#include "base/check_deref.h" #include "base/check_deref.h"
#include "chromeos/ui/vector_icons/vector_icons.h" #include "ui/gfx/geometry/rect.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/text_constants.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/background.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/flex_layout_view.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/metadata/view_factory_internal.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
namespace ash::curtain { namespace ash::curtain {
namespace { namespace {
using views::Builder; constexpr char kRemoteManagementCurtainUrl[] = "chrome://security-curtain/";
using views::FlexLayoutView;
using views::ImageView;
using views::kFlexBehaviorKey;
using views::kMarginsKey;
using views::LayoutOrientation;
constexpr gfx::Size kEnterpriseIconSize(40, 40); gfx::Size CalculateCurtainViewSize(const gfx::Size& size) {
constexpr gfx::Size kLockImageSize(300, 300); // TODO(b/271099991): Use correct margins once Oobe code has migrated to the
// new UX style so we can use their code.
constexpr gfx::Insets kEnterpriseIconMargin = gfx::Insets::VH(20, 5); const int horizontal_margin = size.width() / 10;
constexpr gfx::Insets kLockImageMargin = gfx::Insets::VH(20, 20); const int vertical_margin = size.height() / 10;
constexpr gfx::Insets kLeftSideMargins = gfx::Insets::VH(200, 100); return gfx::Size(size.width() - 2 * horizontal_margin,
constexpr gfx::Insets kRightSideMargins = gfx::Insets::VH(100, 100); size.height() - 2 * vertical_margin);
gfx::ImageSkia EnterpriseIcon(const ColorProvider& color_provider) {
return gfx::CreateVectorIcon(
chromeos::kEnterpriseIcon,
color_provider.GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary));
} }
gfx::ImageSkia LockImage(const ColorProvider& color_provider) {
return gfx::CreateVectorIcon(
kSystemTrayCapsLockIcon,
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorProminent));
}
std::u16string TitleText() {
return l10n_util::GetStringUTF16(IDS_ASH_CURTAIN_TITLE);
}
std::u16string MessageText() {
return l10n_util::GetStringUTF16(IDS_ASH_CURTAIN_DESCRIPTION);
}
// A container that - when added as a child of a `FlexContainer` - will
// automatically resize to take an equal share of the available space.
class ResizingFlexContainer : public views::FlexLayoutView {
public:
ResizingFlexContainer() {
// Tell our parent flex container that we want to be resized depending
// on the available space.
SetProperty(
kFlexBehaviorKey,
views::FlexSpecification{views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded});
}
ResizingFlexContainer(const ResizingFlexContainer&) = delete;
ResizingFlexContainer& operator=(const ResizingFlexContainer&) = delete;
~ResizingFlexContainer() override = default;
// `views::FlexLayoutView` implementation:
gfx::Size CalculatePreferredSize() const override {
// The parent Flex container will first grant each child as much space
// as their preferred size, and then distributes all remaining space
// equally among all children. So to ensure all children get exactly the
// same space, we make them all report the same (small) preferred size.
return gfx::Size(1, 1);
}
};
BEGIN_VIEW_BUILDER(, ResizingFlexContainer, views::FlexLayoutView)
END_VIEW_BUILDER
} // namespace } // namespace
} // namespace ash::curtain
// Allow `ResizingContainer` to be used inside a view builder hierarchy
// (`Builder<ResizingContainer>().SetXYZ()`).
//
// Must be in the global namespace.
DEFINE_VIEW_BUILDER(, ash::curtain::ResizingFlexContainer)
namespace ash::curtain {
RemoteMaintenanceCurtainView::RemoteMaintenanceCurtainView() { RemoteMaintenanceCurtainView::RemoteMaintenanceCurtainView() {
Initialize(); Initialize();
} }
RemoteMaintenanceCurtainView::~RemoteMaintenanceCurtainView() = default; RemoteMaintenanceCurtainView::~RemoteMaintenanceCurtainView() = default;
void RemoteMaintenanceCurtainView::Initialize() { void RemoteMaintenanceCurtainView::OnBoundsChanged(const gfx::Rect&) {
const ColorProvider& color_provider = CHECK_DEREF(ColorProvider::Get()); UpdateChildrenSize(size());
const int shelf_size = CHECK_DEREF(ShelfConfig::Get()).shelf_size();
// A flex rule forcing the view to maintain its fixed size.
const views::FlexSpecification kFixedSize(
views::MinimumFlexSizeRule::kPreferred,
views::MaximumFlexSizeRule::kPreferred);
Builder<FlexLayoutView>(this)
.SetOrientation(LayoutOrientation::kVertical)
.SetBackground(
views::CreateSolidBackground(color_provider.GetBaseLayerColor(
ColorProvider::BaseLayerType::kOpaque)))
.AddChildren(
// Main content
Builder<ResizingFlexContainer>()
.SetOrientation(LayoutOrientation::kHorizontal)
.AddChildren(
// Left half of the screen
Builder<ResizingFlexContainer>()
.SetProperty(kMarginsKey, kLeftSideMargins)
.SetOrientation(LayoutOrientation::kVertical)
.AddChildren(
// Enterprise icon
Builder<ImageView>()
.SetImage(EnterpriseIcon(color_provider))
.SetImageSize(kEnterpriseIconSize)
.SetProperty(kMarginsKey, kEnterpriseIconMargin)
.SetHorizontalAlignment(
ImageView::Alignment::kLeading),
// Title
Builder<views::Label>()
.SetText(TitleText())
.SetTextStyle(views::style::STYLE_EMPHASIZED)
.SetTextContext(
views::style::CONTEXT_DIALOG_TITLE)
.SetHorizontalAlignment(
gfx::HorizontalAlignment::ALIGN_LEFT)
.SetMultiLine(true)
.SetEnabledColor(
color_provider.GetContentLayerColor(
ColorProvider::ContentLayerType::
kTextColorPrimary)),
// Message
Builder<views::Label>()
.SetText(MessageText())
.SetVerticalAlignment(
gfx::VerticalAlignment::ALIGN_TOP)
.SetHorizontalAlignment(
gfx::HorizontalAlignment::ALIGN_LEFT)
.SetMultiLine(true)
.SetEnabledColor(
color_provider.GetContentLayerColor(
ColorProvider::ContentLayerType::
kTextColorPrimary))),
// Right half of the screen
Builder<ResizingFlexContainer>()
.SetProperty(kMarginsKey, kRightSideMargins)
.AddChildren(
Builder<ImageView>()
.SetImage(LockImage(color_provider))
.SetImageSize(kLockImageSize)
.SetProperty(kMarginsKey, kLockImageMargin)
.SetHorizontalAlignment(
ImageView::Alignment::kCenter))),
// Shelf
Builder<View>()
.SetPreferredSize(gfx::Size(0, shelf_size))
.SetProperty(kFlexBehaviorKey, kFixedSize))
.BuildChildren();
} }
BEGIN_METADATA(RemoteMaintenanceCurtainView, views::FlexLayoutView) void RemoteMaintenanceCurtainView::UpdateChildrenSize(
END_METADATA const gfx::Size& new_size) {
wallpaper_view_->SetPreferredSize(new_size);
curtain_view_->SetPreferredSize(CalculateCurtainViewSize(new_size));
}
void RemoteMaintenanceCurtainView::Initialize() {
layout_ = SetLayoutManager(std::make_unique<StackLayout>());
AddWallpaper();
AddCurtainWebView();
}
void RemoteMaintenanceCurtainView::AddWallpaper() {
// Add a copy of the wallpaper above the security curtain, since UX wants
// to see a blurred wallpaper but we can't expose the real wallpaper as that
// would require the security curtain to be translucent which would defeat
// its whole purpose.
DCHECK(!wallpaper_view_);
wallpaper_view_ = AddChildView(
std::make_unique<WallpaperView>(wallpaper_constants::kLockLoginBlur));
}
void RemoteMaintenanceCurtainView::AddCurtainWebView() {
DCHECK(!curtain_view_);
curtain_view_ =
AddChildView(AshWebViewFactory::Get()->Create(AshWebView::InitParams()));
layout_->SetVerticalAlignmentForView(curtain_view_,
StackLayout::VerticalAlignment::kCenter);
// Load the actual security curtain content.
curtain_view_->Navigate(GURL(kRemoteManagementCurtainUrl));
}
} // namespace ash::curtain } // namespace ash::curtain

@@ -6,15 +6,25 @@
#define ASH_CURTAIN_REMOTE_MAINTENANCE_CURTAIN_VIEW_H_ #define ASH_CURTAIN_REMOTE_MAINTENANCE_CURTAIN_VIEW_H_
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ui/base/metadata/metadata_header_macros.h" #include "base/allocator/partition_allocator/pointers/raw_ptr.h"
#include "ui/views/layout/flex_layout_view.h" #include "ui/views/view.h"
namespace ash {
class AshWebView;
class StackLayout;
} // namespace ash
namespace gfx {
class Rect;
class Size;
} // namespace gfx
namespace ash::curtain { namespace ash::curtain {
// The root view shown as the security curtain overlay when the security curtain // The root view shown as the security curtain overlay when the security curtain
// is created by an enterprise admin through the 'start crd session' remote // is created by an enterprise admin through the 'start crd session' remote
// command. // command.
class ASH_EXPORT RemoteMaintenanceCurtainView : public views::FlexLayoutView { class ASH_EXPORT RemoteMaintenanceCurtainView : public views::View {
public: public:
RemoteMaintenanceCurtainView(); RemoteMaintenanceCurtainView();
RemoteMaintenanceCurtainView(const RemoteMaintenanceCurtainView&) = delete; RemoteMaintenanceCurtainView(const RemoteMaintenanceCurtainView&) = delete;
@@ -22,10 +32,19 @@ class ASH_EXPORT RemoteMaintenanceCurtainView : public views::FlexLayoutView {
delete; delete;
~RemoteMaintenanceCurtainView() override; ~RemoteMaintenanceCurtainView() override;
METADATA_HEADER(RemoteMaintenanceCurtainView);
private: private:
// `views::View` implementation:
void OnBoundsChanged(const gfx::Rect&) override;
void UpdateChildrenSize(const gfx::Size& new_size);
void Initialize(); void Initialize();
void AddWallpaper();
void AddCurtainWebView();
raw_ptr<StackLayout> layout_ = nullptr;
raw_ptr<AshWebView> curtain_view_ = nullptr;
raw_ptr<views::View> wallpaper_view_ = nullptr;
}; };
} // namespace ash::curtain } // namespace ash::curtain

@@ -6648,4 +6648,13 @@ Your <ph name="DEVICE_TYPE">{0}<ex>Chromebook</ex></ph> will be locked now.
<message name="IDS_OFFICE_FALLBACK_INSTRUCTIONS" desc="Office Fallback dialog instructions for the choices (buttons) the user has available to open the office file."> <message name="IDS_OFFICE_FALLBACK_INSTRUCTIONS" desc="Office Fallback dialog instructions for the choices (buttons) the user has available to open the office file.">
Choose "Try again", or choose "Open in offline editor" to use limited view and editing options. Choose "Try again", or choose "Open in offline editor" to use limited view and editing options.
</message> </message>
<!-- security curtain -->
<message name="IDS_SECURITY_CURTAIN_TITLE" desc="Title text displayed to inform the user a remote admin has taken control of the ChromeOs device.">
Your administrator is controlling your device
</message>
<message name="IDS_SECURITY_CURTAIN_DESCRIPTION" desc="Detailed description displayed to inform the user a remote admin has taken control of the ChromeOs device.">
Your administrator has logged in to look into an issue. You can continue to use the device after the administrator gives the control back to you.
</message>
</grit-part> </grit-part>

@@ -0,0 +1 @@
344f76c78773402b51bc14353489b58efc19569a

@@ -0,0 +1 @@
344f76c78773402b51bc14353489b58efc19569a

@@ -0,0 +1,27 @@
# 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.
import("//ui/webui/resources/tools/build_webui.gni")
assert(is_chromeos_ash)
build_webui("build") {
grd_prefix = "remote_maintenance_curtain"
static_files = [
"main.html",
# Vector resources
"images/admin_control_dark.svg",
"images/admin_control_light.svg",
]
# Files added here must have a corresponding .html file
web_component_files = [ "curtain_screen.ts" ]
ts_deps = [
"//third_party/polymer/v3_0:library",
"//ui/webui/resources/js:build_ts",
]
}

@@ -0,0 +1 @@
file://ash/curtain/OWNERS

@@ -0,0 +1,21 @@
<!--
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.
-->
<style include="oobe-common-styles oobe-dialog-host-styles"></style>
<oobe-adaptive-dialog id="mainCurtainDialog" role="dialog"
aria-label="$i18n{curtainTitle}">
<iron-icon slot="icon" icon="oobe-32:enterprise"></iron-icon>
<h1 slot="title">$i18n{curtainTitle}</h1>
<div slot="subtitle">$i18n{curtainDescription}</div>
<div slot="content" class="flex layout vertical center center-justified">
<picture>
<source srcset="images/admin_control_dark.svg"
media="(prefers-color-scheme: dark)" class="oobe-illustration">
<img class="illustration" src="images/admin_control_light.svg">
</picture>
</div>
</oobe-adaptive-dialog>

@@ -0,0 +1,73 @@
// 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.
import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
import '//resources/polymer/v3_0/iron-media-query/iron-media-query.js';
import './components/common_styles/oobe_dialog_host_styles.css.js';
import './components/dialogs/oobe_adaptive_dialog.js';
import './components/oobe_icons.html.js';
import './components/common_styles/oobe_common_styles.css.js';
import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './curtain_screen.html.js';
function setDialogSizeAndOrientation() {
document.documentElement.style.setProperty(
'--oobe-oobe-dialog-height-base', window.innerHeight + 'px');
document.documentElement.style.setProperty(
'--oobe-oobe-dialog-width-base', window.innerWidth + 'px');
// Screen orientation value needs to be kept up-to-date.
document.documentElement.setAttribute(
'orientation',
window.innerWidth > window.innerHeight ? 'horizontal' : 'vertical');
}
// TODO(b/274059668): Remove when OOBE is migrated to TS.
type OobeAdaptiveDialog = HTMLElement&{
onBeforeShow(): void,
};
// Inform the compiler that the`this.$.mainCurtainDialog` statement below
// returns an element of type `OobeAdaptiveDialog`.
interface CurtainScreenElement {
$: {
mainCurtainDialog: OobeAdaptiveDialog,
};
}
class CurtainScreenElement extends PolymerElement {
static get is() {
return 'curtain-screen' as const;
}
static get template() {
return getTemplate();
}
override ready() {
super.ready();
this.$.mainCurtainDialog.onBeforeShow();
setDialogSizeAndOrientation();
}
override connectedCallback() {
super.connectedCallback();
window.addEventListener('resize', setDialogSizeAndOrientation);
}
override disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener('resize', setDialogSizeAndOrientation);
}
}
declare global {
interface HTMLElementTagNameMap {
[CurtainScreenElement.is]: CurtainScreenElement;
}
}
customElements.define(CurtainScreenElement.is, CurtainScreenElement);

@@ -0,0 +1 @@
<svg width="512" height="260" viewBox="0 0 512 260" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M200 229v-58.398c0-20.72-16.76-37.602-37.5-37.602-20.663 0-37.5 16.806-37.5 37.602V229" stroke="#5BB974" stroke-width="2" stroke-miterlimit="10"/><path d="M302 117v11m6-16v16" stroke="#FCC934" stroke-width="2" stroke-miterlimit="10"/><path d="M204 35v10m-6-14v14" stroke="#669DF6" stroke-width="2" stroke-miterlimit="10"/><path d="M267.923 229H178V58.308C178 51 183.918 45 191.296 45h63.408C262.005 45 268 50.923 268 58.308V229h-.077Z" fill="#8AB4F8" fill-opacity=".4"/><path d="M378.291 126h-82.658c-4.813 0-8.633 3.938-8.633 8.725V229h100v-94.275a8.686 8.686 0 0 0-8.709-8.725Z" fill="#669DF6"/><path d="M228 229v-8.39c0-2.569 2.093-4.61 4.729-4.61h10.542c2.636 0 4.729 2.041 4.729 4.61V229" stroke="#669DF6" stroke-width="2" stroke-miterlimit="10"/><path d="M363 229v-9.994c0-1.653-1.385-3.006-3.077-3.006h-7.846c-1.692 0-3.077 1.353-3.077 3.006V229m-34 0v-6.744h-7.518v-7.365h-7.441v-8.527h-6.904V199H287" stroke="#323336" stroke-width="2" stroke-miterlimit="10"/><path d="M287 199h-10v30" stroke="#8AB4F8" stroke-opacity=".4" stroke-width="2" stroke-miterlimit="10"/><path fill-rule="evenodd" clip-rule="evenodd" d="M178 137.457v-2.199c13.556 5.988 23 19.586 23 35.344V229h-2v-58.398c0-14.63-8.583-27.292-21-33.145Z" fill="#323336"/><path d="M225 62h-7v12h7V62Zm0 25h-7v12h7V87Zm0 25h-7v11h7v-11Z" fill="#669DF6" stroke="#669DF6" stroke-width="2" stroke-miterlimit="10"/><path d="M225 137h-7v11h7v-11Z" fill="#323336" stroke="#323336" stroke-width="2" stroke-miterlimit="10"/><path d="M225 161h-7v12h7v-12Zm0 25h-7v12h7v-12Zm16-124h-6v12h6V62Zm0 25h-6v12h6V87Zm0 25h-6v11h6v-11Zm0 25h-6v11h6v-11Zm0 24h-6v12h6v-12Z" fill="#669DF6" stroke="#669DF6" stroke-width="2" stroke-miterlimit="10"/><path d="M241 186h-6v12h6v-12Z" fill="#323336" stroke="#323336" stroke-width="2" stroke-miterlimit="10"/><path d="M258 62h-7v12h7V62Z" fill="#669DF6" stroke="#669DF6" stroke-width="2" stroke-miterlimit="10"/><path d="M258 87h-7v12h7V87Z" fill="#323336" stroke="#323336" stroke-width="2" stroke-miterlimit="10"/><path d="M258 112h-7v11h7v-11Zm0 25h-7v11h7v-11Zm0 24h-7v12h7v-12Zm0 25h-7v12h7v-12Z" fill="#669DF6" stroke="#669DF6" stroke-width="2" stroke-miterlimit="10"/><path d="M326 137h61m-61 11h61m-61 12h61m-61 12h61m-61 11h61m-61 12h61m-61 12h61m-61-81v103" stroke="#323336" stroke-width="2" stroke-miterlimit="10"/><path stroke="#669DF6" stroke-width="2" stroke-linecap="round" d="M36 228h440"/></svg>

After

(image error) Size: 2.5 KiB

@@ -0,0 +1 @@
<svg width="512" height="260" viewBox="0 0 512 260" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M200 229v-58.398c0-20.72-16.76-37.602-37.5-37.602-20.663 0-37.5 16.806-37.5 37.602V229" stroke="#34A853" stroke-width="2" stroke-miterlimit="10"/><path d="M302 117v11m6-16v16" stroke="#FBBC04" stroke-width="2" stroke-miterlimit="10"/><path d="M204 35v11m-6-15v15" stroke="#4285F4" stroke-width="2" stroke-miterlimit="10"/><path d="M267.923 229H178V58.308C178 51 183.918 45 191.296 45h63.408C262.005 45 268 50.923 268 58.308V229h-.077Z" fill="#D2E3FC"/><path d="M378.291 126h-82.658c-4.813 0-8.633 3.938-8.633 8.725V229h100v-94.275a8.686 8.686 0 0 0-8.709-8.725Z" fill="#4285F4"/><path d="M228 229v-8.39c0-2.569 2.093-4.61 4.729-4.61h10.542c2.636 0 4.729 2.041 4.729 4.61V229" stroke="#4285F4" stroke-width="2" stroke-miterlimit="10"/><path d="M363 229v-9.994c0-1.653-1.385-3.006-3.077-3.006h-7.846c-1.692 0-3.077 1.353-3.077 3.006V229" stroke="#D2E3FC" stroke-width="2" stroke-miterlimit="10"/><path d="M315 229v-6.744h-7.518v-7.365h-7.441v-8.527h-6.904V199H287" stroke="#fff" stroke-width="2" stroke-miterlimit="10"/><path d="M287 199h-10v30" stroke="#D2E3FC" stroke-width="2" stroke-miterlimit="10"/><path fill-rule="evenodd" clip-rule="evenodd" d="M178 137.457v-2.2c13.556 5.989 23 19.587 23 35.345V229h-2v-58.398c0-14.63-8.583-27.293-21-33.145Z" fill="#fff"/><path d="M225 62h-7v12h7V62Zm0 25h-7v12h7V87Zm0 25h-7v11h7v-11Z" fill="#4285F4" stroke="#4285F4" stroke-width="2" stroke-miterlimit="10"/><path d="M225 137h-7v11h7v-11Z" fill="#fff" stroke="#fff" stroke-width="2" stroke-miterlimit="10"/><path d="M225 161h-7v12h7v-12Zm0 25h-7v12h7v-12Zm16-124h-6v12h6V62Zm0 25h-6v12h6V87Zm0 25h-6v11h6v-11Zm0 25h-6v11h6v-11Zm0 24h-6v12h6v-12Z" fill="#4285F4" stroke="#4285F4" stroke-width="2" stroke-miterlimit="10"/><path d="M241 186h-6v12h6v-12Z" fill="#fff" stroke="#fff" stroke-width="2" stroke-miterlimit="10"/><path d="M258 62h-7v12h7V62Z" fill="#4285F4" stroke="#4285F4" stroke-width="2" stroke-miterlimit="10"/><path d="M258 87h-7v12h7V87Z" fill="#fff" stroke="#fff" stroke-width="2" stroke-miterlimit="10"/><path d="M258 112h-7v11h7v-11Zm0 25h-7v11h7v-11Zm0 24h-7v12h7v-12Zm0 25h-7v12h7v-12Z" fill="#4285F4" stroke="#4285F4" stroke-width="2" stroke-miterlimit="10"/><path d="M326 137h61m-61 11h61m-61 12h61m-61 12h61m-61 11h61m-61 12h61m-61 12h61m-61-81v103" stroke="#D2E3FC" stroke-width="2" stroke-miterlimit="10"/><path stroke="#4285F4" stroke-width="2" stroke-linecap="round" d="M36 228h440"/></svg>

After

(image error) Size: 2.5 KiB

@@ -0,0 +1,23 @@
<!--
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.
-->
<!doctype html>
<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<link rel="stylesheet"
href="chrome://resources/chromeos/colors/cros_styles.css">
<link rel="stylesheet" href="./oobe.css">
</head>
<body>
<curtain-screen></curtain-screen>
<script type="module" src='./curtain_screen.js'></script>
</body>
</html>

@@ -2964,6 +2964,8 @@ static_library("ui") {
"webui/ash/parent_access/parent_access_ui_handler_impl.h", "webui/ash/parent_access/parent_access_ui_handler_impl.h",
"webui/ash/power_ui.cc", "webui/ash/power_ui.cc",
"webui/ash/power_ui.h", "webui/ash/power_ui.h",
"webui/ash/remote_maintenance_curtain_ui.cc",
"webui/ash/remote_maintenance_curtain_ui.h",
"webui/ash/set_time_ui.cc", "webui/ash/set_time_ui.cc",
"webui/ash/set_time_ui.h", "webui/ash/set_time_ui.h",
"webui/ash/shimless_rma_dialog.cc", "webui/ash/shimless_rma_dialog.cc",
@@ -3271,6 +3273,7 @@ static_library("ui") {
"//chrome/browser/resources:internet_detail_dialog_resources", "//chrome/browser/resources:internet_detail_dialog_resources",
"//chrome/browser/resources/chromeos:multidevice_setup_resources", "//chrome/browser/resources/chromeos:multidevice_setup_resources",
"//chrome/browser/resources/chromeos/cloud_upload:resources", "//chrome/browser/resources/chromeos/cloud_upload:resources",
"//chrome/browser/resources/chromeos/remote_maintenance_curtain:resources",
"//chrome/browser/ui/quick_answers", "//chrome/browser/ui/quick_answers",
"//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings", "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings",
"//chrome/browser/ui/webui/ash/audio:mojo_bindings", "//chrome/browser/ui/webui/ash/audio:mojo_bindings",

@@ -0,0 +1,41 @@
// 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/webui/ash/remote_maintenance_curtain_ui.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/remote_maintenance_curtain_resources.h"
#include "chrome/grit/remote_maintenance_curtain_resources_map.h"
#include "content/public/browser/web_ui_data_source.h"
namespace ash {
RemoteMaintenanceCurtainUI::RemoteMaintenanceCurtainUI(content::WebUI* web_ui)
: content::WebUIController(web_ui) {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui), chrome::kChromeUIRemoteManagementCurtainHost);
webui::SetupWebUIDataSource(
source,
base::make_span(kRemoteMaintenanceCurtainResources,
kRemoteMaintenanceCurtainResourcesSize),
IDR_REMOTE_MAINTENANCE_CURTAIN_MAIN_HTML);
// Add OOBE resources so our WebUI can find the OOBE WebUI resources (css,
// javascript files, ...) at runtime.
OobeUI::AddOobeComponents(source);
// Add localized strings
source->AddLocalizedString("curtainTitle", IDS_SECURITY_CURTAIN_TITLE);
source->AddLocalizedString("curtainDescription",
IDS_SECURITY_CURTAIN_DESCRIPTION);
}
WEB_UI_CONTROLLER_TYPE_IMPL(RemoteMaintenanceCurtainUI)
} // namespace ash

@@ -0,0 +1,28 @@
// 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.
#ifndef CHROME_BROWSER_UI_WEBUI_ASH_REMOTE_MAINTENANCE_CURTAIN_UI_H_
#define CHROME_BROWSER_UI_WEBUI_ASH_REMOTE_MAINTENANCE_CURTAIN_UI_H_
#include "content/public/browser/web_ui_controller.h"
namespace ash {
class RemoteMaintenanceCurtainUI : public content::WebUIController {
public:
explicit RemoteMaintenanceCurtainUI(content::WebUI* web_ui);
RemoteMaintenanceCurtainUI(const RemoteMaintenanceCurtainUI&) = delete;
RemoteMaintenanceCurtainUI& operator=(const RemoteMaintenanceCurtainUI&) =
delete;
~RemoteMaintenanceCurtainUI() override = default;
private:
WEB_UI_CONTROLLER_TYPE_DECL();
};
} // namespace ash
#endif // CHROME_BROWSER_UI_WEBUI_ASH_REMOTE_MAINTENANCE_CURTAIN_UI_H_

@@ -290,6 +290,7 @@
#include "chrome/browser/ui/webui/ash/notification_tester/notification_tester_ui.h" #include "chrome/browser/ui/webui/ash/notification_tester/notification_tester_ui.h"
#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h" #include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.h"
#include "chrome/browser/ui/webui/ash/power_ui.h" #include "chrome/browser/ui/webui/ash/power_ui.h"
#include "chrome/browser/ui/webui/ash/remote_maintenance_curtain_ui.h"
#include "chrome/browser/ui/webui/ash/set_time_ui.h" #include "chrome/browser/ui/webui/ash/set_time_ui.h"
#include "chrome/browser/ui/webui/ash/slow_trace_ui.h" #include "chrome/browser/ui/webui/ash/slow_trace_ui.h"
#include "chrome/browser/ui/webui/ash/slow_ui.h" #include "chrome/browser/ui/webui/ash/slow_ui.h"
@@ -1012,6 +1013,9 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
} }
return nullptr; return nullptr;
} }
if (url.host_piece() == chrome::kChromeUIRemoteManagementCurtainHost) {
return &NewWebUI<ash::RemoteMaintenanceCurtainUI>;
}
if (url.host_piece() == ash::kChromeUIDiagnosticsAppHost) { if (url.host_piece() == ash::kChromeUIDiagnosticsAppHost) {
return &NewWebUI<ash::DiagnosticsDialogUI>; return &NewWebUI<ash::DiagnosticsDialogUI>;
} }

@@ -308,6 +308,7 @@ template("chrome_extra_paks") {
"$root_gen_dir/chrome/oobe_unconditional_resources.pak", "$root_gen_dir/chrome/oobe_unconditional_resources.pak",
"$root_gen_dir/chrome/os_settings_resources.pak", "$root_gen_dir/chrome/os_settings_resources.pak",
"$root_gen_dir/chrome/password_change_resources.pak", "$root_gen_dir/chrome/password_change_resources.pak",
"$root_gen_dir/chrome/remote_maintenance_curtain_resources.pak",
"$root_gen_dir/chrome/supervision_resources.pak", "$root_gen_dir/chrome/supervision_resources.pak",
"$root_gen_dir/chrome/vc_tray_tester_resources.pak", "$root_gen_dir/chrome/vc_tray_tester_resources.pak",
"$root_gen_dir/chromeos/ash/ash_resources.pak", "$root_gen_dir/chromeos/ash/ash_resources.pak",
@@ -383,6 +384,7 @@ template("chrome_extra_paks") {
"//chrome/browser/resources/chromeos/network_ui:resources", "//chrome/browser/resources/chromeos/network_ui:resources",
"//chrome/browser/resources/chromeos/notification_tester:resources", "//chrome/browser/resources/chromeos/notification_tester:resources",
"//chrome/browser/resources/chromeos/password_change:resources", "//chrome/browser/resources/chromeos/password_change:resources",
"//chrome/browser/resources/chromeos/remote_maintenance_curtain:resources",
"//chrome/browser/resources/chromeos/supervision:resources", "//chrome/browser/resources/chromeos/supervision:resources",
"//chrome/browser/resources/chromeos/vc_tray_tester:resources", "//chrome/browser/resources/chromeos/vc_tray_tester:resources",
"//chrome/browser/resources/nearby_internals:resources", "//chrome/browser/resources/nearby_internals:resources",

@@ -377,6 +377,7 @@ const char kChromeUIPasswordChangeUrl[] = "chrome://password-change";
const char kChromeUIPrintManagementUrl[] = "chrome://print-management"; const char kChromeUIPrintManagementUrl[] = "chrome://print-management";
const char kChromeUIPowerHost[] = "power"; const char kChromeUIPowerHost[] = "power";
const char kChromeUIPowerUrl[] = "chrome://power"; const char kChromeUIPowerUrl[] = "chrome://power";
const char kChromeUIRemoteManagementCurtainHost[] = "security-curtain";
const char kChromeUIScanningAppURL[] = "chrome://scanning"; const char kChromeUIScanningAppURL[] = "chrome://scanning";
const char kChromeUIScreenlockIconHost[] = "screenlock-icon"; const char kChromeUIScreenlockIconHost[] = "screenlock-icon";
const char kChromeUIScreenlockIconURL[] = "chrome://screenlock-icon/"; const char kChromeUIScreenlockIconURL[] = "chrome://screenlock-icon/";
@@ -482,8 +483,9 @@ bool IsSystemWebUIHost(base::StringPiece host) {
#endif // BUILDFLAG(PLATFORM_CFM) #endif // BUILDFLAG(PLATFORM_CFM)
}; };
for (const char* h : kHosts) { for (const char* h : kHosts) {
if (host == h) if (host == h) {
return true; return true;
}
} }
return false; return false;
} }

@@ -352,6 +352,7 @@ extern const char kChromeUIPasswordChangeUrl[];
extern const char kChromeUIPrintManagementUrl[]; extern const char kChromeUIPrintManagementUrl[];
extern const char kChromeUIPowerHost[]; extern const char kChromeUIPowerHost[];
extern const char kChromeUIPowerUrl[]; extern const char kChromeUIPowerUrl[];
extern const char kChromeUIRemoteManagementCurtainHost[];
extern const char kChromeUIScanningAppURL[]; extern const char kChromeUIScanningAppURL[];
extern const char kChromeUIScreenlockIconHost[]; extern const char kChromeUIScreenlockIconHost[];
extern const char kChromeUIScreenlockIconURL[]; extern const char kChromeUIScreenlockIconURL[];

@@ -151,6 +151,10 @@
"META": {"sizes": {"includes": [50]}}, "META": {"sizes": {"includes": [50]}},
"includes": [1380], "includes": [1380],
}, },
"<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/remote_maintenance_curtain/resources.grd": {
"META": {"sizes": {"includes": [10]}},
"includes": [1381],
},
"<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/supervision/resources.grd": { "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/supervision/resources.grd": {
"META": {"sizes": {"includes": [10],}}, "META": {"sizes": {"includes": [10],}},
"includes": [1383], "includes": [1383],