Commit d03bafa9 authored by Alexander Hendrich's avatar Alexander Hendrich Committed by Commit Bot

[Extensions] Add chrome.loginScreenUi API

This CL adds a new extension API "loginScreenUi", which can be used to
display custom UI on the login screen. The generated window will only
be visible on the login and lock screen.
This API will only be available to whitelisted extensions of type
"login_screen_extension".

API proposal: go/api-login-screen-ui
Design doc: go/dd-login-screen-extension-ui

Bug: 957573
Change-Id: Ie9f936af9fd21e9b324f5bbbd8448790f8d33c4d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1556806
Commit-Queue: Alexander Hendrich <hendrich@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664501}
parent 707e0daf
...@@ -3673,6 +3673,9 @@ are declared in tools/grit/grit_rule.gni. ...@@ -3673,6 +3673,9 @@ are declared in tools/grit/grit_rule.gni.
<message name="IDS_EXTENSION_PROMPT_WARNING_INPUT" desc="Permission string for access to input."> <message name="IDS_EXTENSION_PROMPT_WARNING_INPUT" desc="Permission string for access to input.">
Read and change anything you type Read and change anything you type
</message> </message>
<message name="IDS_EXTENSION_PROMPT_WARNING_LOGIN_SCREEN_UI" desc="Permission string for access to login screen UI.">
Display UI on the login screen
</message>
<message name="IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT" desc="Permisson string for access to extension management."> <message name="IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT" desc="Permisson string for access to extension management.">
Manage your apps, extensions, and themes Manage your apps, extensions, and themes
</message> </message>
......
...@@ -2089,6 +2089,8 @@ source_set("chromeos") { ...@@ -2089,6 +2089,8 @@ source_set("chromeos") {
"extensions/input_method_api.h", "extensions/input_method_api.h",
"extensions/login_screen_ui/login_screen_extension_ui_handler.cc", "extensions/login_screen_ui/login_screen_extension_ui_handler.cc",
"extensions/login_screen_ui/login_screen_extension_ui_handler.h", "extensions/login_screen_ui/login_screen_extension_ui_handler.h",
"extensions/login_screen_ui/login_screen_ui_api.cc",
"extensions/login_screen_ui/login_screen_ui_api.h",
"extensions/media_player_api.cc", "extensions/media_player_api.cc",
"extensions/media_player_api.h", "extensions/media_player_api.h",
"extensions/quick_unlock_private/quick_unlock_private_api.cc", "extensions/quick_unlock_private/quick_unlock_private_api.cc",
......
...@@ -9,11 +9,13 @@ ...@@ -9,11 +9,13 @@
#include "ash/public/cpp/login_types.h" #include "ash/public/cpp/login_types.h"
#include "chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_window.h" #include "chrome/browser/chromeos/login/ui/login_screen_extension_ui/login_screen_extension_ui_window.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/ui/ash/login_screen_client.h"
#include "components/session_manager/core/session_manager.h" #include "components/session_manager/core/session_manager.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/permissions/permissions_data.h"
namespace chromeos { namespace chromeos {
...@@ -28,10 +30,11 @@ const char kErrorNotOnLoginOrLockScreen[] = ...@@ -28,10 +30,11 @@ const char kErrorNotOnLoginOrLockScreen[] =
LoginScreenExtensionUiHandler* g_instance = nullptr; LoginScreenExtensionUiHandler* g_instance = nullptr;
bool CanUseLoginScreenUiApi(const extensions::Extension* extension) { bool CanUseLoginScreenUiApi(const extensions::Extension* extension) {
// TODO(hendrich) also check permission in CL 1556806
return extensions::ExtensionRegistry::Get(ProfileHelper::GetSigninProfile()) return extensions::ExtensionRegistry::Get(ProfileHelper::GetSigninProfile())
->enabled_extensions() ->enabled_extensions()
.Contains(extension->id()); .Contains(extension->id()) &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kLoginScreenUi);
} }
} // namespace } // namespace
......
...@@ -13,11 +13,13 @@ ...@@ -13,11 +13,13 @@
#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h" #include "chrome/test/base/testing_profile_manager.h"
#include "components/session_manager/core/session_manager.h" #include "components/session_manager/core/session_manager.h"
#include "components/version_info/version_info.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_service_manager_context.h" #include "content/public/test/test_service_manager_context.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h" #include "extensions/common/extension_builder.h"
#include "extensions/common/features/feature_channel.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace { namespace {
...@@ -31,6 +33,12 @@ const char kErrorNotOnLoginOrLockScreen[] = ...@@ -31,6 +33,12 @@ const char kErrorNotOnLoginOrLockScreen[] =
const bool kCanBeClosedByUser = false; const bool kCanBeClosedByUser = false;
const char kUrl[] = "test.html"; const char kUrl[] = "test.html";
const char kWhitelistedExtensionID1[] =
"oclffehlkdgibkainkilopaalpdobkan"; // chrome.loginScreenUi test extension
const char kWhitelistedExtensionID2[] =
"lpimkpkllnkdlcigdbgmabfplniahkgm"; // Imprivata (login screen)
const char kPermissionName[] = "loginScreenUi";
} // namespace } // namespace
namespace chromeos { namespace chromeos {
...@@ -83,7 +91,8 @@ class FakeLoginScreenExtensionUiWindowFactory ...@@ -83,7 +91,8 @@ class FakeLoginScreenExtensionUiWindowFactory
class LoginScreenExtensionUiHandlerUnittest : public testing::Test { class LoginScreenExtensionUiHandlerUnittest : public testing::Test {
public: public:
LoginScreenExtensionUiHandlerUnittest() LoginScreenExtensionUiHandlerUnittest()
: profile_manager_(TestingBrowserProcess::GetGlobal()) {} : scoped_current_channel_(version_info::Channel::DEV),
profile_manager_(TestingBrowserProcess::GetGlobal()) {}
~LoginScreenExtensionUiHandlerUnittest() override = default; ~LoginScreenExtensionUiHandlerUnittest() override = default;
void SetUp() override { void SetUp() override {
...@@ -107,7 +116,10 @@ class LoginScreenExtensionUiHandlerUnittest : public testing::Test { ...@@ -107,7 +116,10 @@ class LoginScreenExtensionUiHandlerUnittest : public testing::Test {
session_manager::SessionState::LOGIN_PRIMARY); session_manager::SessionState::LOGIN_PRIMARY);
extension_ = extensions::ExtensionBuilder("test" /*extension_name*/) extension_ = extensions::ExtensionBuilder("test" /*extension_name*/)
.SetID("abcdefghijklmnopqrstuvwxyzabcdef") .SetID(kWhitelistedExtensionID1)
.SetLocation(extensions::Manifest::EXTERNAL_POLICY)
.AddPermission(kPermissionName)
.AddFlags(extensions::Extension::FOR_LOGIN_SCREEN)
.Build(); .Build();
extension_registry_->AddEnabled(extension_); extension_registry_->AddEnabled(extension_);
} }
...@@ -169,6 +181,7 @@ class LoginScreenExtensionUiHandlerUnittest : public testing::Test { ...@@ -169,6 +181,7 @@ class LoginScreenExtensionUiHandlerUnittest : public testing::Test {
content::TestBrowserThreadBundle thread_bundle_; content::TestBrowserThreadBundle thread_bundle_;
content::TestServiceManagerContext context_; content::TestServiceManagerContext context_;
const extensions::ScopedCurrentChannel scoped_current_channel_;
session_manager::SessionManager session_manager_; session_manager::SessionManager session_manager_;
TestingProfileManager profile_manager_; TestingProfileManager profile_manager_;
...@@ -264,7 +277,10 @@ TEST_F(LoginScreenExtensionUiHandlerUnittest, WindowClosedOnUnlock) { ...@@ -264,7 +277,10 @@ TEST_F(LoginScreenExtensionUiHandlerUnittest, WindowClosedOnUnlock) {
TEST_F(LoginScreenExtensionUiHandlerUnittest, TwoExtensionsInParallel) { TEST_F(LoginScreenExtensionUiHandlerUnittest, TwoExtensionsInParallel) {
scoped_refptr<const extensions::Extension> other_extension = scoped_refptr<const extensions::Extension> other_extension =
extensions::ExtensionBuilder("other extension" /*extension_name*/) extensions::ExtensionBuilder("other extension" /*extension_name*/)
.SetID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") .SetID(kWhitelistedExtensionID2)
.SetLocation(extensions::Manifest::EXTERNAL_POLICY)
.AddPermission(kPermissionName)
.AddFlags(extensions::Extension::FOR_LOGIN_SCREEN)
.Build(); .Build();
extension_registry_->AddEnabled(other_extension); extension_registry_->AddEnabled(other_extension);
...@@ -329,16 +345,32 @@ TEST_F(LoginScreenExtensionUiHandlerUnittest, ...@@ -329,16 +345,32 @@ TEST_F(LoginScreenExtensionUiHandlerUnittest,
EXPECT_TRUE(ui_handler_->HasOpenWindow(extension_->id())); EXPECT_TRUE(ui_handler_->HasOpenWindow(extension_->id()));
} }
TEST_F(LoginScreenExtensionUiHandlerDeathUnittest, NotFromSigninProfile) { TEST_F(LoginScreenExtensionUiHandlerDeathUnittest, NotAllowed) {
// |other_extension| is not enabled in the sign-in profile's extensions // |other_profile_extension| is not enabled in the sign-in profile's
// registry. // extensions registry.
scoped_refptr<const extensions::Extension> other_extension = scoped_refptr<const extensions::Extension> other_profile_extension =
extensions::ExtensionBuilder("other extension" /*extension_name*/) extensions::ExtensionBuilder("other profile" /*extension_name*/)
.SetID("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") .SetID(kWhitelistedExtensionID2) // whitelisted
.SetLocation(extensions::Manifest::EXTERNAL_POLICY)
.AddPermission(kPermissionName)
.AddFlags(extensions::Extension::FOR_LOGIN_SCREEN)
.Build();
CheckCannotUseAPI(other_profile_extension.get());
CheckCannotUseAPI(other_profile_extension.get());
// |no_permission_extension| is enabled in the sign-in profile's extensions
// registry, but doesn't have the needed "loginScreenUi" permission.
scoped_refptr<const extensions::Extension> no_permission_extension =
extensions::ExtensionBuilder("no permission extension" /*extension_name*/)
.SetID(kWhitelistedExtensionID2) // whitelisted
.SetLocation(extensions::Manifest::EXTERNAL_POLICY)
.AddFlags(extensions::Extension::FOR_LOGIN_SCREEN)
.Build(); .Build();
extension_registry_->AddEnabled(no_permission_extension);
CheckCannotUseAPI(other_extension.get()); CheckCannotUseAPI(no_permission_extension.get());
CheckCannotUseAPI(other_extension.get()); CheckCannotUseAPI(no_permission_extension.get());
} }
} // namespace chromeos } // namespace chromeos
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.h"
#include "chrome/browser/chromeos/extensions/login_screen_ui/login_screen_extension_ui_handler.h"
#include "chrome/common/extensions/api/login_screen_ui.h"
namespace login_screen_ui = extensions::api::login_screen_ui;
namespace extensions {
LoginScreenUiShowFunction::LoginScreenUiShowFunction() = default;
LoginScreenUiShowFunction::~LoginScreenUiShowFunction() = default;
ExtensionFunction::ResponseAction LoginScreenUiShowFunction::Run() {
std::unique_ptr<login_screen_ui::Show::Params> parameters =
login_screen_ui::Show::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(parameters);
const login_screen_ui::ShowOptions& options = parameters->options;
bool user_can_close =
options.user_can_close ? *options.user_can_close : false;
std::string error;
bool success =
chromeos::LoginScreenExtensionUiHandler::Get(true /*can_create*/)
->Show(extension(), options.url, user_can_close, &error);
if (!success)
return RespondNow(Error(error));
return RespondNow(NoArguments());
}
LoginScreenUiCloseFunction::LoginScreenUiCloseFunction() = default;
LoginScreenUiCloseFunction::~LoginScreenUiCloseFunction() = default;
ExtensionFunction::ResponseAction LoginScreenUiCloseFunction::Run() {
std::string error;
bool success =
chromeos::LoginScreenExtensionUiHandler::Get(true /*can_create*/)
->Close(extension(), &error);
if (!success)
return RespondNow(Error(error));
return RespondNow(NoArguments());
}
} // namespace extensions
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
#include "extensions/browser/extension_function.h"
namespace extensions {
class LoginScreenUiShowFunction : public UIThreadExtensionFunction {
public:
LoginScreenUiShowFunction();
DECLARE_EXTENSION_FUNCTION("loginScreenUi.show", LOGINSCREENUI_SHOW)
protected:
~LoginScreenUiShowFunction() override;
// ExtensionFunction:
ResponseAction Run() override;
private:
DISALLOW_COPY_AND_ASSIGN(LoginScreenUiShowFunction);
};
class LoginScreenUiCloseFunction : public UIThreadExtensionFunction {
public:
LoginScreenUiCloseFunction();
DECLARE_EXTENSION_FUNCTION("loginScreenUi.close", LOGINSCREENUI_CLOSE)
protected:
~LoginScreenUiCloseFunction() override;
// ExtensionFunction:
ResponseAction Run() override;
private:
DISALLOW_COPY_AND_ASSIGN(LoginScreenUiCloseFunction);
};
} // namespace extensions
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_UI_LOGIN_SCREEN_UI_API_H_
...@@ -541,6 +541,10 @@ ...@@ -541,6 +541,10 @@
"dependencies": ["permission:launcherSearchProvider"], "dependencies": ["permission:launcherSearchProvider"],
"contexts": ["blessed_extension"] "contexts": ["blessed_extension"]
}, },
"loginScreenUi": {
"dependencies": ["permission:loginScreenUi"],
"contexts": ["blessed_extension"]
},
"webcamPrivate": { "webcamPrivate": {
"dependencies": ["permission:webcamPrivate"], "dependencies": ["permission:webcamPrivate"],
"contexts": ["blessed_extension"] "contexts": ["blessed_extension"]
......
...@@ -435,6 +435,16 @@ ...@@ -435,6 +435,16 @@
// (staging) // (staging)
] ]
}, },
"loginScreenUi": {
"channel": "dev",
"extension_types": ["login_screen_extension"],
"location": "policy",
"platforms": ["chromeos"],
"whitelist": [
"7FE4A999359A456C4B0FB7B7AD85CEA29CA50519", // chrome.loginScreenUi test extension
"E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE" // Imprivata (login screen)
]
},
"webcamPrivate": { "webcamPrivate": {
"channel": "stable", "channel": "stable",
"extension_types": ["extension", "platform_app"], "extension_types": ["extension", "platform_app"],
......
...@@ -97,6 +97,7 @@ if (is_chromeos) { ...@@ -97,6 +97,7 @@ if (is_chromeos) {
"input_ime.json", "input_ime.json",
"input_method_private.json", "input_method_private.json",
"launcher_search_provider.idl", "launcher_search_provider.idl",
"login_screen_ui.idl",
"platform_keys.idl", "platform_keys.idl",
"platform_keys_internal.idl", "platform_keys_internal.idl",
"quick_unlock_private.idl", "quick_unlock_private.idl",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Use the <code>chrome.loginScreenUi</code> API to show and hide custom
// login UI.
[platforms=("chromeos"),
implemented_in="chrome/browser/chromeos/extensions/login_screen_ui/login_screen_ui_api.h"]
namespace loginScreenUi {
dictionary ShowOptions {
// Relative url of the contents to show.
DOMString url;
// Whether the user can close the window, defaults to false.
boolean? userCanClose;
};
// Callback that does not take arguments.
callback SimpleCallback = void ();
interface Functions {
// Opens a window, which is visible on top of the login and lock screen.
// |options|: Options for the custom login UI window.
static void show(ShowOptions options, optional SimpleCallback callback);
// Closes the login/lock screen UI window previously opened by this
// extension.
static void close(optional SimpleCallback callback);
};
};
...@@ -654,6 +654,9 @@ ChromePermissionMessageRule::GetAllRules() { ...@@ -654,6 +654,9 @@ ChromePermissionMessageRule::GetAllRules() {
{IDS_EXTENSION_PROMPT_WARNING_ENTERPRISE_HARDWARE_PLATFORM, {IDS_EXTENSION_PROMPT_WARNING_ENTERPRISE_HARDWARE_PLATFORM,
{APIPermission::kEnterpriseHardwarePlatform}, {APIPermission::kEnterpriseHardwarePlatform},
{}}, {}},
{IDS_EXTENSION_PROMPT_WARNING_LOGIN_SCREEN_UI,
{APIPermission::kLoginScreenUi},
{}},
}; };
return std::vector<ChromePermissionMessageRule>( return std::vector<ChromePermissionMessageRule>(
......
...@@ -1401,6 +1401,8 @@ enum HistogramValue { ...@@ -1401,6 +1401,8 @@ enum HistogramValue {
FILEMANAGERPRIVATE_SETARCSTORAGETOASTSHOWNFLAG = 1338, FILEMANAGERPRIVATE_SETARCSTORAGETOASTSHOWNFLAG = 1338,
INPUTMETHODPRIVATE_SETCOMPOSITIONRANGE = 1339, INPUTMETHODPRIVATE_SETCOMPOSITIONRANGE = 1339,
BLUETOOTHPRIVATE_RECORDRECONNECTION = 1340, BLUETOOTHPRIVATE_RECORDRECONNECTION = 1340,
LOGINSCREENUI_SHOW = 1341,
LOGINSCREENUI_CLOSE = 1342,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -374,7 +374,7 @@ bool ExtensionCanLoadInIncognito(bool is_main_frame, ...@@ -374,7 +374,7 @@ bool ExtensionCanLoadInIncognito(bool is_main_frame,
bool extension_enabled_in_incognito) { bool extension_enabled_in_incognito) {
if (!extension || !extension_enabled_in_incognito) if (!extension || !extension_enabled_in_incognito)
return false; return false;
if (!is_main_frame) if (!is_main_frame || extension->is_login_screen_extension())
return true; return true;
// Only allow incognito toplevel navigations to extension resources in // Only allow incognito toplevel navigations to extension resources in
......
...@@ -44,6 +44,8 @@ bool IsIncognitoEnabled(const std::string& extension_id, ...@@ -44,6 +44,8 @@ bool IsIncognitoEnabled(const std::string& extension_id,
// work in incognito mode. // work in incognito mode.
if (Manifest::IsComponentLocation(extension->location())) if (Manifest::IsComponentLocation(extension->location()))
return true; return true;
if (extension->is_login_screen_extension())
return true;
} }
return ExtensionPrefs::Get(context)->IsIncognitoEnabled(extension_id); return ExtensionPrefs::Get(context)->IsIncognitoEnabled(extension_id);
} }
......
...@@ -423,8 +423,17 @@ ...@@ -423,8 +423,17 @@
}, },
"runtime": { "runtime": {
"channel": "stable", "channel": "stable",
"extension_types": ["extension", "legacy_packaged_app", "platform_app"], "extension_types": [
"contexts": ["blessed_extension", "lock_screen_extension", "extension_service_worker"] "extension",
"legacy_packaged_app",
"platform_app",
"login_screen_extension"
],
"contexts": [
"blessed_extension",
"lock_screen_extension",
"extension_service_worker"
]
}, },
"runtime.getManifest": { "runtime.getManifest": {
"contexts": [ "contexts": [
......
...@@ -258,6 +258,7 @@ class APIPermission { ...@@ -258,6 +258,7 @@ class APIPermission {
kSystemPowerSource = 214, kSystemPowerSource = 214,
kArcAppsPrivate = 215, kArcAppsPrivate = 215,
kEnterpriseHardwarePlatform = 216, kEnterpriseHardwarePlatform = 216,
kLoginScreenUi = 217,
// Last entry: Add new entries above and ensure to update the // Last entry: Add new entries above and ensure to update the
// "ExtensionPermission3" enum in tools/metrics/histograms/histograms.xml // "ExtensionPermission3" enum in tools/metrics/histograms/histograms.xml
// (by running update_extension_permission.py). // (by running update_extension_permission.py).
......
...@@ -78,6 +78,7 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = { ...@@ -78,6 +78,7 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
{APIPermission::kLockScreen, "lockScreen"}, {APIPermission::kLockScreen, "lockScreen"},
{APIPermission::kLockWindowFullscreenPrivate, "lockWindowFullscreenPrivate", {APIPermission::kLockWindowFullscreenPrivate, "lockWindowFullscreenPrivate",
APIPermissionInfo::kFlagCannotBeOptional}, APIPermissionInfo::kFlagCannotBeOptional},
{APIPermission::kLoginScreenUi, "loginScreenUi"},
{APIPermission::kMediaPerceptionPrivate, "mediaPerceptionPrivate"}, {APIPermission::kMediaPerceptionPrivate, "mediaPerceptionPrivate"},
{APIPermission::kMetricsPrivate, "metricsPrivate", {APIPermission::kMetricsPrivate, "metricsPrivate",
APIPermissionInfo::kFlagCannotBeOptional}, APIPermissionInfo::kFlagCannotBeOptional},
......
...@@ -19491,6 +19491,8 @@ Called by update_net_error_codes.py.--> ...@@ -19491,6 +19491,8 @@ Called by update_net_error_codes.py.-->
<int value="1338" label="FILEMANAGERPRIVATE_SETARCSTORAGETOASTSHOWNFLAG"/> <int value="1338" label="FILEMANAGERPRIVATE_SETARCSTORAGETOASTSHOWNFLAG"/>
<int value="1339" label="INPUTMETHODPRIVATE_SETCOMPOSITIONRANGE"/> <int value="1339" label="INPUTMETHODPRIVATE_SETCOMPOSITIONRANGE"/>
<int value="1340" label="BLUETOOTHPRIVATE_RECORDRECONNECTION"/> <int value="1340" label="BLUETOOTHPRIVATE_RECORDRECONNECTION"/>
<int value="1341" label="LOGINSCREENUI_SHOW"/>
<int value="1342" label="LOGINSCREENUI_CLOSE"/>
</enum> </enum>
<enum name="ExtensionIconState"> <enum name="ExtensionIconState">
...@@ -20017,6 +20019,7 @@ Called by update_net_error_codes.py.--> ...@@ -20017,6 +20019,7 @@ Called by update_net_error_codes.py.-->
<int value="214" label="kSystemPowerSource"/> <int value="214" label="kSystemPowerSource"/>
<int value="215" label="kArcAppsPrivate"/> <int value="215" label="kArcAppsPrivate"/>
<int value="216" label="kEnterpriseHardwarePlatform"/> <int value="216" label="kEnterpriseHardwarePlatform"/>
<int value="217" label="kLoginScreenUi"/>
</enum> </enum>
<enum name="ExtensionServiceVerifyAllSuccess"> <enum name="ExtensionServiceVerifyAllSuccess">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment