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
</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 -->
<message name="IDS_ASH_PAGINATION_LEFT_ARROW_TOOLTIP" desc="The tooltip text used for pagination left arrow button." >
Previous page

@ -1 +0,0 @@
1a2410a9c35ae9f30af8833c20f46f7490f09839

@ -1 +0,0 @@
1a2410a9c35ae9f30af8833c20f46f7490f09839

@ -6,195 +6,75 @@
#include <memory>
#include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/style/color_provider.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/assistant/ui/base/stack_layout.h"
#include "ash/public/cpp/ash_web_view.h"
#include "ash/public/cpp/ash_web_view_factory.h"
#include "ash/wallpaper/wallpaper_constants.h"
#include "ash/wallpaper/wallpaper_view.h"
#include "base/check_deref.h"
#include "chromeos/ui/vector_icons/vector_icons.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/rect.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_class_properties.h"
namespace ash::curtain {
namespace {
using views::Builder;
using views::FlexLayoutView;
using views::ImageView;
using views::kFlexBehaviorKey;
using views::kMarginsKey;
using views::LayoutOrientation;
constexpr char kRemoteManagementCurtainUrl[] = "chrome://security-curtain/";
constexpr gfx::Size kEnterpriseIconSize(40, 40);
constexpr gfx::Size kLockImageSize(300, 300);
constexpr gfx::Insets kEnterpriseIconMargin = gfx::Insets::VH(20, 5);
constexpr gfx::Insets kLockImageMargin = gfx::Insets::VH(20, 20);
constexpr gfx::Insets kLeftSideMargins = gfx::Insets::VH(200, 100);
constexpr gfx::Insets kRightSideMargins = gfx::Insets::VH(100, 100);
gfx::ImageSkia EnterpriseIcon(const ColorProvider& color_provider) {
return gfx::CreateVectorIcon(
chromeos::kEnterpriseIcon,
color_provider.GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary));
gfx::Size CalculateCurtainViewSize(const gfx::Size& size) {
// TODO(b/271099991): Use correct margins once Oobe code has migrated to the
// new UX style so we can use their code.
const int horizontal_margin = size.width() / 10;
const int vertical_margin = size.height() / 10;
return gfx::Size(size.width() - 2 * horizontal_margin,
size.height() - 2 * vertical_margin);
}
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 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() {
Initialize();
}
RemoteMaintenanceCurtainView::~RemoteMaintenanceCurtainView() = default;
void RemoteMaintenanceCurtainView::Initialize() {
const ColorProvider& color_provider = CHECK_DEREF(ColorProvider::Get());
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();
void RemoteMaintenanceCurtainView::OnBoundsChanged(const gfx::Rect&) {
UpdateChildrenSize(size());
}
BEGIN_METADATA(RemoteMaintenanceCurtainView, views::FlexLayoutView)
END_METADATA
void RemoteMaintenanceCurtainView::UpdateChildrenSize(
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

@ -6,15 +6,25 @@
#define ASH_CURTAIN_REMOTE_MAINTENANCE_CURTAIN_VIEW_H_
#include "ash/ash_export.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/layout/flex_layout_view.h"
#include "base/allocator/partition_allocator/pointers/raw_ptr.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 {
// 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
// command.
class ASH_EXPORT RemoteMaintenanceCurtainView : public views::FlexLayoutView {
class ASH_EXPORT RemoteMaintenanceCurtainView : public views::View {
public:
RemoteMaintenanceCurtainView();
RemoteMaintenanceCurtainView(const RemoteMaintenanceCurtainView&) = delete;
@ -22,10 +32,19 @@ class ASH_EXPORT RemoteMaintenanceCurtainView : public views::FlexLayoutView {
delete;
~RemoteMaintenanceCurtainView() override;
METADATA_HEADER(RemoteMaintenanceCurtainView);
private:
// `views::View` implementation:
void OnBoundsChanged(const gfx::Rect&) override;
void UpdateChildrenSize(const gfx::Size& new_size);
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

@ -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.">
Choose "Try again", or choose "Open in offline editor" to use limited view and editing options.
</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>

@ -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/power_ui.cc",
"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.h",
"webui/ash/shimless_rma_dialog.cc",
@ -3271,6 +3273,7 @@ static_library("ui") {
"//chrome/browser/resources:internet_detail_dialog_resources",
"//chrome/browser/resources/chromeos:multidevice_setup_resources",
"//chrome/browser/resources/chromeos/cloud_upload:resources",
"//chrome/browser/resources/chromeos/remote_maintenance_curtain:resources",
"//chrome/browser/ui/quick_answers",
"//chrome/browser/ui/webui/ash/add_supervision: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/parent_access/parent_access_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/slow_trace_ui.h"
#include "chrome/browser/ui/webui/ash/slow_ui.h"
@ -1012,6 +1013,9 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
}
return nullptr;
}
if (url.host_piece() == chrome::kChromeUIRemoteManagementCurtainHost) {
return &NewWebUI<ash::RemoteMaintenanceCurtainUI>;
}
if (url.host_piece() == ash::kChromeUIDiagnosticsAppHost) {
return &NewWebUI<ash::DiagnosticsDialogUI>;
}

@ -308,6 +308,7 @@ template("chrome_extra_paks") {
"$root_gen_dir/chrome/oobe_unconditional_resources.pak",
"$root_gen_dir/chrome/os_settings_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/vc_tray_tester_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/notification_tester: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/vc_tray_tester: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 kChromeUIPowerHost[] = "power";
const char kChromeUIPowerUrl[] = "chrome://power";
const char kChromeUIRemoteManagementCurtainHost[] = "security-curtain";
const char kChromeUIScanningAppURL[] = "chrome://scanning";
const char kChromeUIScreenlockIconHost[] = "screenlock-icon";
const char kChromeUIScreenlockIconURL[] = "chrome://screenlock-icon/";
@ -482,8 +483,9 @@ bool IsSystemWebUIHost(base::StringPiece host) {
#endif // BUILDFLAG(PLATFORM_CFM)
};
for (const char* h : kHosts) {
if (host == h)
if (host == h) {
return true;
}
}
return false;
}

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

@ -151,6 +151,10 @@
"META": {"sizes": {"includes": [50]}},
"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": {
"META": {"sizes": {"includes": [10],}},
"includes": [1383],