Commit f55c1658 authored by Danan S's avatar Danan S Committed by Commit Bot

Create dialog that shows a Parent Permissions prompt

This dialog's purpose is to prompt a parent of a child-type user to
enter their credentials to approve an action.  This dialog will also
allow you to pass in an extension::Extension, whose details (i.e.
permissions, title) will be displayed above the credential entry form.

This CL is inspired by the functionality of
ExtensionInstallPrompt, but simplifies the approach used by
that code.

Bug: 957832

Change-Id: I3a9395f56505b43cd5c8f8c269b0b19c99ce6379
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1972417
Commit-Queue: Dan S <danan@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747083}
parent d5c6382e
...@@ -4433,6 +4433,36 @@ Keep your key file in a safe place. You will need it to create new versions of y ...@@ -4433,6 +4433,36 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME" desc="Text for the install button on the extension install prompt, for a theme."> <message name="IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME" desc="Text for the install button on the extension install prompt, for a theme.">
Add theme Add theme
</message> </message>
<message name="IDS_EXTENSION_INSTALL_PROMPT_ASK_A_PARENT_BUTTON" desc="Text for the ask-a-parent button on the extension install prompt.">
Ask a parent
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_GO_GET_A_PARENT_FOR_EXTENSION_LABEL" desc="Text for the title section of the parent permission dialog asking child to get a parent to approve.">
Go get a parent to approve "<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>"
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_EXTENSION_TYPE_EXTENSION" desc="The 'extension' extension type">
extension
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_EXTENSION_TYPE_APP" desc="The 'app' extension type">
app
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_CHILD_WANTS_TO_INSTALL_LABEL" desc="Text for the header section of the parent permission dialog explaining that the child is installing a extension that is requesting permissions.">
<ph name="CHILD_NAME">$1<ex>Junior</ex></ph> wants to install an <ph name="EXTENSION_TYPE">$2<ex>extension</ex></ph> that can:
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_APPROVE_BUTTON" desc="Text for the approve button on the parent permission prompt.">
Approve
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_CANCEL_BUTTON" desc="Text for the cancel button on the parent permission prompt.">
Cancel
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_SELECT_PARENT_LABEL" desc="Text for the label shown above the parent selector.">
Choose parent account for approval
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_ENTER_PASSWORD_LABEL" desc="Text for the label shown above the parent password entry field.">
Enter password
</message>
<message name="IDS_PARENT_PERMISSION_PROMPT_PASSWORD_INCORRECT_LABEL" desc="Text for the label shown when the previously entered password was incorrect.">
Password incorrect
</message>
<message name="IDS_EXTENSION_INSTALL_PROMPT_REQUEST_BUTTON" desc="Text for the request button on the extension request prompt."> <message name="IDS_EXTENSION_INSTALL_PROMPT_REQUEST_BUTTON" desc="Text for the request button on the extension request prompt.">
Send Send
</message> </message>
......
0d31c9725e5c8598a949a0d6cb5d8a0d9e925609
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
966c9f8f99d307802f89a8ccdd34833d21e28f0c
\ No newline at end of file
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/extensions/extension_install_ui_factory.h" #include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
#include "chrome/common/buildflags.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h" #include "chrome/grit/theme_resources.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
...@@ -185,12 +186,20 @@ base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const { ...@@ -185,12 +186,20 @@ base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
switch (type_) { switch (type_) {
case INSTALL_PROMPT: case INSTALL_PROMPT:
case WEBSTORE_WIDGET_PROMPT: case WEBSTORE_WIDGET_PROMPT:
if (extension_->is_app()) #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
if (user_is_child()) {
id = IDS_EXTENSION_INSTALL_PROMPT_ASK_A_PARENT_BUTTON;
} else
#endif
// NOTE: strange indentation formatting is due to intervening
// BUILDFLAG above.
if (extension_->is_app()) {
id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP; id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
else if (extension_->is_theme()) } else if (extension_->is_theme()) {
id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME; id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
else } else {
id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION; id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
}
break; break;
case RE_ENABLE_PROMPT: case RE_ENABLE_PROMPT:
id = IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON; id = IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "chrome/browser/extensions/install_prompt_permissions.h" #include "chrome/browser/extensions/install_prompt_permissions.h"
#include "chrome/common/buildflags.h"
#include "extensions/common/permissions/permission_message.h" #include "extensions/common/permissions/permission_message.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
...@@ -107,6 +108,14 @@ class ExtensionInstallPrompt { ...@@ -107,6 +108,14 @@ class ExtensionInstallPrompt {
base::string16 GetRetainedFilesHeading() const; base::string16 GetRetainedFilesHeading() const;
base::string16 GetRetainedDevicesHeading() const; base::string16 GetRetainedDevicesHeading() const;
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
void set_user_is_child(bool user_is_child) {
user_is_child_ = user_is_child;
}
bool user_is_child() const { return user_is_child_; }
#endif
bool ShouldShowPermissions() const; bool ShouldShowPermissions() const;
bool ShouldDisplayWithholdingUI() const; bool ShouldDisplayWithholdingUI() const;
...@@ -167,6 +176,11 @@ class ExtensionInstallPrompt { ...@@ -167,6 +176,11 @@ class ExtensionInstallPrompt {
// permissions if only additional ones are being requested) // permissions if only additional ones are being requested)
extensions::InstallPromptPermissions prompt_permissions_; extensions::InstallPromptPermissions prompt_permissions_;
#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
// True if the current user is a child.
bool user_is_child_ = false;
#endif
bool is_requesting_host_permissions_; bool is_requesting_host_permissions_;
// The extension being installed. // The extension being installed.
......
...@@ -106,6 +106,8 @@ class LoggedInUserMixin : public InProcessBrowserTestMixin { ...@@ -106,6 +106,8 @@ class LoggedInUserMixin : public InProcessBrowserTestMixin {
const AccountId& GetAccountId() { return user_.account_id; } const AccountId& GetAccountId() { return user_.account_id; }
FakeGaiaMixin* GetFakeGaiaMixin() { return &fake_gaia_; }
private: private:
LoginManagerMixin::TestUserInfo user_; LoginManagerMixin::TestUserInfo user_;
LoginManagerMixin login_manager_; LoginManagerMixin login_manager_;
......
...@@ -2684,6 +2684,13 @@ jumbo_static_library("ui") { ...@@ -2684,6 +2684,13 @@ jumbo_static_library("ui") {
} }
} }
if (is_chromeos) {
sources += [
"views/parent_permission_dialog_view.cc",
"views/parent_permission_dialog_view.h",
]
}
if (toolkit_views) { if (toolkit_views) {
sources += [ sources += [
"autofill/payments/local_card_migration_bubble.h", "autofill/payments/local_card_migration_bubble.h",
...@@ -3900,6 +3907,8 @@ jumbo_static_library("ui") { ...@@ -3900,6 +3907,8 @@ jumbo_static_library("ui") {
"ash/launcher/shelf_spinner_controller.h", "ash/launcher/shelf_spinner_controller.h",
"ash/launcher/shelf_spinner_item_controller.cc", "ash/launcher/shelf_spinner_item_controller.cc",
"ash/launcher/shelf_spinner_item_controller.h", "ash/launcher/shelf_spinner_item_controller.h",
"supervised_user/parent_permission_dialog.cc",
"supervised_user/parent_permission_dialog.h",
"views/apps/app_block_dialog_view.cc", "views/apps/app_block_dialog_view.cc",
"views/apps/app_block_dialog_view.h", "views/apps/app_block_dialog_view.h",
"views/apps/app_pause_dialog_view.cc", "views/apps/app_pause_dialog_view.cc",
......
...@@ -270,6 +270,7 @@ enum class DialogIdentifier { ...@@ -270,6 +270,7 @@ enum class DialogIdentifier {
APP_UNINSTALL = 101, APP_UNINSTALL = 101,
PRINT_JOB_CONFIRMATION = 102, PRINT_JOB_CONFIRMATION = 102,
CROSTINI_RECOVERY = 103, CROSTINI_RECOVERY = 103,
PARENT_PERMISSION = 104, // ChromeOS only.
// Add values above this line with a corresponding label in // Add values above this line with a corresponding label in
// tools/metrics/histograms/enums.xml // tools/metrics/histograms/enums.xml
MAX_VALUE MAX_VALUE
......
This diff is collapsed.
// Copyright 2020 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_UI_SUPERVISED_USER_PARENT_PERMISSION_DIALOG_H_
#define CHROME_BROWSER_UI_SUPERVISED_USER_PARENT_PERMISSION_DIALOG_H_
#include <stddef.h>
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/strings/string16.h"
#include "google_apis/gaia/gaia_auth_consumer.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/native_widget_types.h"
class GoogleServiceAuthError;
class Profile;
namespace signin {
class AccessTokenFetcher;
struct AccessTokenInfo;
class IdentityManager;
} // namespace signin
namespace content {
class WebContents;
}
namespace extensions {
class Extension;
}
namespace gfx {
class Image;
}
namespace internal {
struct ParentPermissionDialogViewResult;
}
// ParentPermissionDialog provides a dialog that will prompt a child user's
// parent(s) for their permission for action. The parent(s) approve the action
// by entering their Google password, which is then verified using the Google
// Reauthentication API's child to parent delegation mode. The prompt can only
// be shown if the user is a child. Otherwise, the prompt will fail.
//
// Clients should provide a ParentPermissionDialog::DoneCallback to
// receive the results of the dialog.
// Example Usage:
// ParentPermissionDialog::DoneCallback callback = base::BindOnce(
// &ParentPermissionDialogBrowserTest::OnParentPermissionDialogDone,
// weak_ptr_factory_.GetWeakPtr()))
// ParentPermissionDialog dialog(profile, std::move(callback));
// SkBitmap icon = LoadMyIcon();
// dialog.ShowPrompt(web_contents, "Allow your child to purchase X?", icon);
//
//
// This dialog is currently used to display content relevant for a parent to
// provide permission for the installation of an extension. Using the
// ShowPromptForExtensionInstallation() method below.
//
// This class is not thread safe.
class ParentPermissionDialog : public GaiaAuthConsumer {
public:
enum class Result {
kParentPermissionReceived,
kParentPermissionCanceled,
kParentPermissionFailed,
};
using DoneCallback = base::OnceCallback<void(Result result)>;
ParentPermissionDialog(Profile* profile, DoneCallback callback);
ParentPermissionDialog(const ParentPermissionDialog&) = delete;
ParentPermissionDialog& operator=(const ParentPermissionDialog&) = delete;
~ParentPermissionDialog() override;
// Shows the Parent Permission Dialog.
// |message| specifies the text to be shown in the dialog.
// |icon| specifies the icon to be displayed. It can be empty.
void ShowPrompt(content::WebContents* web_contents,
const base::string16& message,
const SkBitmap& icon);
// Shows the Parent Permission Dialog for the specified extension
// installation. The dialog's message will be generated from the extension
// itself. |fallback_icon| can be empty. If it is set, it will be used as a
// backup in the event that the extension's icon couldn't be loaded from the
// extension itself. If it is empty, and the icon couldn't be loaded from the
// extension, a default generic extension icon will be displayed.
void ShowPromptForExtensionInstallation(
content::WebContents* web_contents,
const extensions::Extension* extension,
const SkBitmap& fallback_icon);
// Sets whether the prompt is shown again automatically after an
// incorrect credential. This defaults to true, and is only disabled for
// testing. Without this, the test will infinitely repeatedly re-prompt
// for a password when it is incorrect.
void set_reprompt_after_incorrect_credential(
bool reprompt_after_incorrect_credential) {
reprompt_after_incorrect_credential_ = reprompt_after_incorrect_credential;
}
static void SetFakeIdentityManagerForTesting(
signin::IdentityManager* identity_manager);
// Only used for testing. Returns true if an invalid credential was received.
bool CredentialWasInvalid() const;
private:
void LoadParentEmailAddresses();
void OnExtensionIconLoaded(const gfx::Image& image);
void LoadExtensionIcon();
// Shows prompt internally. If |show_password_incorrect| is true, a message
// will be displayed indicating that.
void ShowPromptInternal(bool show_password_incorrect);
// Called when the parent permission prompt UI finishes, but before the
// ReAuth process starts.
void OnParentPermissionPromptDone(
internal::ParentPermissionDialogViewResult result);
// Called to handle the case when a user clicks the Accept button in the
// dialog.
void HandleParentPermissionDialogAccepted(
internal::ParentPermissionDialogViewResult result);
// Given an email address of the child's parent, return the parents'
// obfuscated gaia id.
std::string GetParentObfuscatedGaiaID(
const base::string16& parent_email) const;
// Starts the Reauth-scoped OAuth access token fetch process.
void StartReAuthAccessTokenFetch(const std::string& parent_obfuscated_gaia_id,
const std::string& parent_credential);
// Handles the result of the access token
void OnAccessTokenFetchComplete(const std::string& parent_obfuscated_gaia_id,
const std::string& parent_credential,
GoogleServiceAuthError error,
signin::AccessTokenInfo access_token_info);
// Starts the Parent Reauth proof token fetch process.
void StartParentReAuthProofTokenFetch(
const std::string& child_access_token,
const std::string& parent_obfuscated_gaia_id,
const std::string& credential);
void SendResult(Result result);
// GaiaAuthConsumer
void OnReAuthProofTokenSuccess(
const std::string& reauth_proof_token) override;
void OnReAuthProofTokenFailure(
const GaiaAuthConsumer::ReAuthProofTokenStatus error) override;
std::vector<base::string16> parent_permission_email_addresses_;
std::unique_ptr<GaiaAuthFetcher> reauth_token_fetcher_;
// Used to fetch OAuth2 access tokens.
signin::IdentityManager* identity_manager_ = nullptr;
std::unique_ptr<signin::AccessTokenFetcher> oauth2_access_token_fetcher_;
Profile* const profile_;
DoneCallback callback_;
const extensions::Extension* extension_ = nullptr;
gfx::ImageSkia icon_;
base::string16 message_;
content::WebContents* web_contents_ = nullptr;
// If true, the prompt will be shown again after an incorrect password
// is entered.
bool reprompt_after_incorrect_credential_ = true;
// Callback to call to close the underlying dialog view.
base::OnceClosure close_dialog_view_callback_;
bool invalid_credential_received_ = false;
base::WeakPtrFactory<ParentPermissionDialog> weak_factory_{this};
};
// NOTE: DO NOT USE the following code directly. It is an implementation detail
// of the dialog. Instead use ParentPermissionDialog above.
namespace internal {
// Internal struct use by the view that implements the dialog to
// communicate the result status of the dialog UI itself.
struct ParentPermissionDialogViewResult {
public:
enum class Status {
kAccepted,
kCanceled,
kUnknown,
};
Status status = Status::kUnknown;
base::string16 selected_parent_permission_email;
base::string16 parent_permission_credential;
};
} // namespace internal
// Implemented by the platform specific ui code to actually show the dialog.
// |window| should be the window to which the dialog is modal. It comes from
// whatever widget is associated with opening the parent permission dialog.
// Returns a closure that should be used to close the dialog view if the caller
// disappears. If |show_parent_password_incorrect| is set to true, then the
// dialog will also display a "Password Incorrect" message.
base::OnceClosure ShowParentPermissionDialog(
Profile* profile,
gfx::NativeWindow window,
const std::vector<base::string16>& parent_permission_email_addresses,
bool show_parent_password_incorrect,
const gfx::ImageSkia& icon,
const base::string16& message,
const extensions::Extension* extension,
base::OnceCallback<void(internal::ParentPermissionDialogViewResult result)>
view_done_callback);
// Only to be used by tests. Sets the next status returned by the dialog
// widget.
void SetAutoConfirmParentPermissionDialogForTest(
internal::ParentPermissionDialogViewResult::Status status);
#endif // CHROME_BROWSER_UI_SUPERVISED_USER_PARENT_PERMISSION_DIALOG_H_
// Copyright 2020 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/ui/supervised_user/parent_permission_dialog.h"
#include <memory>
#include <vector>
#include "base/run_loop.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/supervised_user/logged_in_user_mixin.h"
#include "chrome/browser/supervised_user/supervised_user_features.h"
#include "chrome/browser/supervised_user/supervised_user_service.h"
#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/test_utils.h"
#include "extensions/common/extension_builder.h"
#include "google_apis/gaia/fake_gaia.h"
// End to end test of ParentPermissionDialog that exercises the dialog's
// internal logic the orchestrates the parental permission process.
class ParentPermissionDialogBrowserTest
: public MixinBasedInProcessBrowserTest {
public:
ParentPermissionDialogBrowserTest() = default;
ParentPermissionDialogBrowserTest(const ParentPermissionDialogBrowserTest&) =
delete;
ParentPermissionDialogBrowserTest& operator=(
const ParentPermissionDialogBrowserTest&) = delete;
void OnParentPermissionDialogDone(base::OnceClosure quit_closure,
ParentPermissionDialog::Result result) {
result_ = result;
std::move(quit_closure).Run();
}
void InitializeFamilyData() {
// Set up the child user's custodians (AKA parents).
ASSERT_TRUE(browser());
PrefService* prefs = browser()->profile()->GetPrefs();
prefs->SetString(prefs::kSupervisedUserCustodianEmail,
"test_parent_0@google.com");
prefs->SetString(prefs::kSupervisedUserCustodianObfuscatedGaiaId,
"239029320");
prefs->SetString(prefs::kSupervisedUserSecondCustodianEmail,
"test_parent_1@google.com");
prefs->SetString(prefs::kSupervisedUserSecondCustodianObfuscatedGaiaId,
"85948533");
// Set up the identity test environment, which provides fake
// OAuth refresh tokens.
identity_test_env_ = std::make_unique<signin::IdentityTestEnvironment>();
identity_test_env_->MakeAccountAvailable(
chromeos::FakeGaiaMixin::kFakeUserEmail);
identity_test_env_->SetPrimaryAccount(
chromeos::FakeGaiaMixin::kFakeUserEmail);
identity_test_env_->SetRefreshTokenForPrimaryAccount();
identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
ParentPermissionDialog::SetFakeIdentityManagerForTesting(
identity_test_env_->identity_manager());
}
void SetUpOnMainThread() override {
MixinBasedInProcessBrowserTest::SetUpOnMainThread();
logged_in_user_mixin_.LogInUser(true /* issue_any_scope_token */);
InitializeFamilyData();
SupervisedUserService* service =
SupervisedUserServiceFactory::GetForProfile(browser()->profile());
service->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(
true);
}
void SetNextReAuthStatus(
const GaiaAuthConsumer::ReAuthProofTokenStatus next_status) {
logged_in_user_mixin_.GetFakeGaiaMixin()->fake_gaia()->SetNextReAuthStatus(
next_status);
}
void ShowPrompt() {
base::RunLoop run_loop;
parent_permission_dialog_ = std::make_unique<ParentPermissionDialog>(
browser()->profile(),
base::BindOnce(
&ParentPermissionDialogBrowserTest::OnParentPermissionDialogDone,
base::Unretained(this), run_loop.QuitClosure()));
parent_permission_dialog_->set_reprompt_after_incorrect_credential(false);
SkBitmap icon =
*gfx::Image(extensions::util::GetDefaultExtensionIcon()).ToSkBitmap();
parent_permission_dialog_->ShowPrompt(
browser()->tab_strip_model()->GetActiveWebContents(),
base::UTF8ToUTF16("Test Prompt Message"), icon);
run_loop.Run();
}
void ShowPromptForExtension(
scoped_refptr<const extensions::Extension> extension) {
base::RunLoop run_loop;
parent_permission_dialog_ = std::make_unique<ParentPermissionDialog>(
browser()->profile(),
base::BindOnce(
&ParentPermissionDialogBrowserTest::OnParentPermissionDialogDone,
base::Unretained(this), run_loop.QuitClosure()));
parent_permission_dialog_->set_reprompt_after_incorrect_credential(false);
SkBitmap icon =
*gfx::Image(extensions::util::GetDefaultExtensionIcon()).ToSkBitmap();
parent_permission_dialog_->ShowPromptForExtensionInstallation(
browser()->tab_strip_model()->GetActiveWebContents(), extension.get(),
icon);
run_loop.Run();
}
void CheckResult(ParentPermissionDialog::Result expected) {
EXPECT_EQ(result_, expected);
}
void CheckInvalidCredentialWasReceived() {
EXPECT_TRUE(parent_permission_dialog_->CredentialWasInvalid());
}
private:
ParentPermissionDialog::Result result_;
std::unique_ptr<ParentPermissionDialog> parent_permission_dialog_;
chromeos::LoggedInUserMixin logged_in_user_mixin_{
&mixin_host_, chromeos::LoggedInUserMixin::LogInType::kChild,
embedded_test_server(), this};
std::unique_ptr<signin::IdentityTestEnvironment> identity_test_env_;
};
IN_PROC_BROWSER_TEST_F(ParentPermissionDialogBrowserTest, PermissionReceived) {
SetNextReAuthStatus(GaiaAuthConsumer::ReAuthProofTokenStatus::kSuccess);
SetAutoConfirmParentPermissionDialogForTest(
internal::ParentPermissionDialogViewResult::Status::kAccepted);
ShowPrompt();
CheckResult(ParentPermissionDialog::Result::kParentPermissionReceived);
}
IN_PROC_BROWSER_TEST_F(ParentPermissionDialogBrowserTest,
PermissionFailedInvalidPassword) {
SetNextReAuthStatus(GaiaAuthConsumer::ReAuthProofTokenStatus::kInvalidGrant);
SetAutoConfirmParentPermissionDialogForTest(
internal::ParentPermissionDialogViewResult::Status::kAccepted);
ShowPrompt();
CheckInvalidCredentialWasReceived();
CheckResult(ParentPermissionDialog::Result::kParentPermissionFailed);
}
IN_PROC_BROWSER_TEST_F(ParentPermissionDialogBrowserTest,
PermissionDialogCanceled) {
SetAutoConfirmParentPermissionDialogForTest(
internal::ParentPermissionDialogViewResult::Status::kCanceled);
ShowPrompt();
CheckResult(ParentPermissionDialog::Result::kParentPermissionCanceled);
}
IN_PROC_BROWSER_TEST_F(ParentPermissionDialogBrowserTest,
PermissionReceivedForExtension) {
SetNextReAuthStatus(GaiaAuthConsumer::ReAuthProofTokenStatus::kSuccess);
SetAutoConfirmParentPermissionDialogForTest(
internal::ParentPermissionDialogViewResult::Status::kAccepted);
ShowPromptForExtension(
extensions::ExtensionBuilder("test extension").Build());
CheckResult(ParentPermissionDialog::Result::kParentPermissionReceived);
}
IN_PROC_BROWSER_TEST_F(ParentPermissionDialogBrowserTest,
PermissionFailedInvalidPasswordForExtension) {
SetNextReAuthStatus(GaiaAuthConsumer::ReAuthProofTokenStatus::kInvalidGrant);
SetAutoConfirmParentPermissionDialogForTest(
internal::ParentPermissionDialogViewResult::Status::kAccepted);
ShowPromptForExtension(
extensions::ExtensionBuilder("test extension").Build());
CheckInvalidCredentialWasReceived();
CheckResult(ParentPermissionDialog::Result::kParentPermissionFailed);
}
IN_PROC_BROWSER_TEST_F(ParentPermissionDialogBrowserTest,
PermissionDialogCanceledForExtension) {
SetAutoConfirmParentPermissionDialogForTest(
internal::ParentPermissionDialogViewResult::Status::kCanceled);
ShowPromptForExtension(
extensions::ExtensionBuilder("test extension").Build());
CheckResult(ParentPermissionDialog::Result::kParentPermissionCanceled);
}
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/ui/views/extensions/extension_permissions_view.h" #include "chrome/browser/ui/views/extensions/extension_permissions_view.h"
#include "chrome/browser/extensions/install_prompt_permissions.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h" #include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/extensions/expandable_container_view.h" #include "chrome/browser/ui/views/extensions/expandable_container_view.h"
...@@ -36,6 +37,13 @@ void ExtensionPermissionsView::AddItem( ...@@ -36,6 +37,13 @@ void ExtensionPermissionsView::AddItem(
} }
} }
void ExtensionPermissionsView::AddPermissions(
const extensions::InstallPromptPermissions& permissions) {
for (size_t i = 0; i < permissions.permissions.size(); ++i) {
AddItem(permissions.permissions[i], permissions.details[i]);
}
}
void ExtensionPermissionsView::ChildPreferredSizeChanged(views::View* child) { void ExtensionPermissionsView::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged(); PreferredSizeChanged();
} }
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
#include "ui/views/view.h" #include "ui/views/view.h"
namespace extensions {
struct InstallPromptPermissions;
} // namespace extensions
// A custom view for the permissions section of the extension info. It contains // A custom view for the permissions section of the extension info. It contains
// the labels for each permission and the views for their associated details, if // the labels for each permission and the views for their associated details, if
// there are any. // there are any.
...@@ -16,9 +20,14 @@ class ExtensionPermissionsView : public views::View { ...@@ -16,9 +20,14 @@ class ExtensionPermissionsView : public views::View {
public: public:
explicit ExtensionPermissionsView(int available_width); explicit ExtensionPermissionsView(int available_width);
// Adds a single pair of |permission_text| and |permission_details| to
// be rendered in the view.
void AddItem(const base::string16& permission_text, void AddItem(const base::string16& permission_text,
const base::string16& permission_details); const base::string16& permission_details);
// Adds the set of |permissions| to be rendered in the view.
void AddPermissions(const extensions::InstallPromptPermissions& permissions);
// views::View: // views::View:
void ChildPreferredSizeChanged(views::View* child) override; void ChildPreferredSizeChanged(views::View* child) override;
......
This diff is collapsed.
// Copyright 2020 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_UI_VIEWS_PARENT_PERMISSION_DIALOG_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_PARENT_PERMISSION_DIALOG_VIEW_H_
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "chrome/browser/extensions/install_prompt_permissions.h"
#include "chrome/browser/ui/supervised_user/parent_permission_dialog.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/view.h"
class Profile;
namespace extensions {
class Extension;
} // namespace extensions
class ParentPermissionSection;
// Modal dialog that shows a dialog that prompts a parent for permission by
// asking them to enter their google account credentials.
class ParentPermissionDialogView : public views::BubbleDialogDelegateView {
public:
using DoneCallback = base::OnceCallback<void(
internal::ParentPermissionDialogViewResult result)>;
struct Params {
Params();
explicit Params(const Params& params);
~Params();
// List of email addresses of parents for whom parent permission for
// installation should be requested. These should be the emails of parent
// accounts that are permitted to approve extension installations
// for the current child user.
std::vector<base::string16> parent_permission_email_addresses;
// If true, shows a message in the parent permission dialog that the
// password entered was incorrect.
bool show_parent_password_incorrect = true;
// The icon to be displayed.
gfx::ImageSkia icon;
// The message to show.
base::string16 message;
// An optional extension whose permissions should be displayed
const extensions::Extension* extension = nullptr;
// The user's profile
Profile* profile = nullptr;
// The parent window to this window.
gfx::NativeWindow window = nullptr;
};
ParentPermissionDialogView(std::unique_ptr<Params> params,
DoneCallback done_callback);
~ParentPermissionDialogView() override;
ParentPermissionDialogView(const ParentPermissionDialogView&) = delete;
ParentPermissionDialogView& operator=(const ParentPermissionDialogView&) =
delete;
// Shows the parent permission dialog.
void ShowDialog();
void set_selected_parent_permission_email_address(
const base::string16& email_address) {
selected_parent_permission_email_ = email_address;
}
void set_parent_permission_credential(const base::string16& credential) {
parent_permission_credential_ = credential;
}
void CloseDialogView();
// TODO(https://crbug.com/1058501): Instead of doing this with closures, use
// an interface shared with the views code whose implementation wraps the
// underlying view, and delete the instance of that interface in the dtor of
// this class.
base::OnceClosure GetCloseDialogClosure();
private:
const Params& params() const { return *params_; }
base::string16 GetActiveUserFirstName() const;
// views::View:
gfx::Size CalculatePreferredSize() const override;
void AddedToWidget() override;
// views::DialogDeleate:
bool Cancel() override;
bool Accept() override;
// views::WidgetDelegate:
base::string16 GetAccessibleWindowTitle() const override;
ui::ModalType GetModalType() const override;
bool ShouldShowCloseButton() const override;
// Changes the widget size to accommodate the contents' preferred size.
void ResizeWidget();
// Creates the contents area that contains permissions and other extension
// info.
void CreateContents();
void ShowDialogInternal();
// Sets the |extension| to be optionally displayed in the dialog. This
// causes the view to show several extension properties including the
// permissions, the icon and the extension name.
void InitializeExtensionData(
scoped_refptr<const extensions::Extension> extension);
// Permissions ot be displayed in the prompt. Only populated
// if an extension has been set.
extensions::InstallPromptPermissions prompt_permissions_;
std::unique_ptr<ParentPermissionSection> parent_permission_section_;
base::string16 selected_parent_permission_email_;
base::string16 parent_permission_credential_;
// Configuration parameters for the prompt.
std::unique_ptr<Params> params_;
DoneCallback done_callback_;
base::WeakPtrFactory<ParentPermissionDialogView> weak_factory_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_PARENT_PERMISSION_DIALOG_VIEW_H_
// Copyright 2020 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/ui/views/parent_permission_dialog_view.h"
#include <memory>
#include <vector>
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/supervised_user/parent_permission_dialog.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
// A simple DialogBrowserTest for the ParentPermissionDialogView that just
// shows the dialog.
class ParentPermissionDialogViewBrowserTest : public DialogBrowserTest {
public:
ParentPermissionDialogViewBrowserTest() {}
ParentPermissionDialogViewBrowserTest(
const ParentPermissionDialogViewBrowserTest&) = delete;
ParentPermissionDialogViewBrowserTest& operator=(
const ParentPermissionDialogViewBrowserTest&) = delete;
void OnParentPermissionPromptDone(
internal::ParentPermissionDialogViewResult result) {}
void ShowUi(const std::string& name) override {
std::vector<base::string16> parent_emails =
std::vector<base::string16>{base::UTF8ToUTF16("parent1@google.com"),
base::UTF8ToUTF16("parent2@google.com")};
gfx::ImageSkia icon = extensions::util::GetDefaultExtensionIcon();
ShowParentPermissionDialog(
browser()->profile(), browser()->window()->GetNativeWindow(),
parent_emails, false, icon, base::UTF8ToUTF16("Test Message"), nullptr,
base::BindOnce(&ParentPermissionDialogViewBrowserTest::
OnParentPermissionPromptDone,
base::Unretained(this)));
}
};
IN_PROC_BROWSER_TEST_F(ParentPermissionDialogViewBrowserTest,
InvokeUi_default) {
ShowAndVerifyUi();
}
...@@ -2454,6 +2454,7 @@ if (!is_android) { ...@@ -2454,6 +2454,7 @@ if (!is_android) {
"../browser/ui/ash/tablet_mode_page_behavior_browsertest.cc", "../browser/ui/ash/tablet_mode_page_behavior_browsertest.cc",
"../browser/ui/ash/volume_controller_browsertest.cc", "../browser/ui/ash/volume_controller_browsertest.cc",
"../browser/ui/browser_finder_chromeos_browsertest.cc", "../browser/ui/browser_finder_chromeos_browsertest.cc",
"../browser/ui/supervised_user/parent_permission_dialog_browsertest.cc",
"../browser/ui/views/apps/app_dialog_view_browsertest.cc", "../browser/ui/views/apps/app_dialog_view_browsertest.cc",
"../browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc", "../browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc",
"../browser/ui/views/arc_app_dialog_view_browsertest.cc", "../browser/ui/views/arc_app_dialog_view_browsertest.cc",
...@@ -2471,6 +2472,7 @@ if (!is_android) { ...@@ -2471,6 +2472,7 @@ if (!is_android) {
"../browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc", "../browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc",
"../browser/ui/views/frame/system_menu_model_builder_browsertest_chromeos.cc", "../browser/ui/views/frame/system_menu_model_builder_browsertest_chromeos.cc",
"../browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc", "../browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc",
"../browser/ui/views/parent_permission_dialog_view_browsertest.cc",
"../browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc", "../browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc",
"../browser/ui/views/web_apps/web_app_ash_interactive_ui_test.cc", "../browser/ui/views/web_apps/web_app_ash_interactive_ui_test.cc",
"../browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc", "../browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc",
......
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