Commit d7682c6e authored by Mohamed Amir Yosef's avatar Mohamed Amir Yosef Committed by Commit Bot

[Passwords] Introduce SaveUpdateWithAccountStore bubble

This is a mechnical fork of the existing SaveUpdate bubble without
any change in functionality.
Follow up CLs will introduce extra funcationlaity and change in the UI
elements.

Bug: 1044038
Change-Id: I1ab6b08a9097b4f7adc06b53317676b42b3a2bf4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2033072Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Commit-Queue: Mohamed Amir Yosef <mamir@chromium.org>
Cr-Commit-Position: refs/heads/master@{#738913}
parent c1aefe52
...@@ -1076,6 +1076,8 @@ jumbo_static_library("ui") { ...@@ -1076,6 +1076,8 @@ jumbo_static_library("ui") {
"passwords/bubble_controllers/password_bubble_controller_base.h", "passwords/bubble_controllers/password_bubble_controller_base.h",
"passwords/bubble_controllers/save_update_bubble_controller.cc", "passwords/bubble_controllers/save_update_bubble_controller.cc",
"passwords/bubble_controllers/save_update_bubble_controller.h", "passwords/bubble_controllers/save_update_bubble_controller.h",
"passwords/bubble_controllers/save_update_with_account_store_bubble_controller.cc",
"passwords/bubble_controllers/save_update_with_account_store_bubble_controller.h",
"passwords/bubble_controllers/sign_in_promo_bubble_controller.cc", "passwords/bubble_controllers/sign_in_promo_bubble_controller.cc",
"passwords/bubble_controllers/sign_in_promo_bubble_controller.h", "passwords/bubble_controllers/sign_in_promo_bubble_controller.h",
"passwords/credential_leak_dialog_controller.h", "passwords/credential_leak_dialog_controller.h",
...@@ -3143,6 +3145,8 @@ jumbo_static_library("ui") { ...@@ -3143,6 +3145,8 @@ jumbo_static_library("ui") {
"views/passwords/password_items_view.h", "views/passwords/password_items_view.h",
"views/passwords/password_save_update_view.cc", "views/passwords/password_save_update_view.cc",
"views/passwords/password_save_update_view.h", "views/passwords/password_save_update_view.h",
"views/passwords/password_save_update_with_account_store_view.cc",
"views/passwords/password_save_update_with_account_store_view.h",
"views/payments/contact_info_editor_view_controller.cc", "views/payments/contact_info_editor_view_controller.cc",
"views/payments/contact_info_editor_view_controller.h", "views/payments/contact_info_editor_view_controller.h",
"views/payments/credit_card_editor_view_controller.cc", "views/payments/credit_card_editor_view_controller.cc",
......
// 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_PASSWORDS_BUBBLE_CONTROLLERS_SAVE_UPDATE_WITH_ACCOUNT_STORE_BUBBLE_CONTROLLER_H_
#define CHROME_BROWSER_UI_PASSWORDS_BUBBLE_CONTROLLERS_SAVE_UPDATE_WITH_ACCOUNT_STORE_BUBBLE_CONTROLLER_H_
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/passwords/bubble_controllers/password_bubble_controller_base.h"
#include "components/password_manager/core/browser/manage_passwords_referrer.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/common/password_manager_ui.h"
class PasswordsModelDelegate;
namespace base {
class Clock;
}
// This controller provides data and actions for the
// PasswordSaveUpdateWithAccountStoreView.
class SaveUpdateWithAccountStoreBubbleController
: public PasswordBubbleControllerBase {
public:
explicit SaveUpdateWithAccountStoreBubbleController(
base::WeakPtr<PasswordsModelDelegate> delegate,
DisplayReason display_reason);
~SaveUpdateWithAccountStoreBubbleController() override;
// Called by the view code when the save/update button is clicked by the user.
void OnSaveClicked();
// Called by the view code when the "Nope" button in clicked by the user in
// update bubble.
void OnNopeUpdateClicked();
// Called by the view code when the "Never for this site." button in clicked
// by the user.
void OnNeverForThisSiteClicked();
// Called by the view code when username or password is corrected using
// the username correction or password selection features in PendingView.
void OnCredentialEdited(base::string16 new_username,
base::string16 new_password);
// The password bubble can switch its state between "save" and "update"
// depending on the user input. |state_| only captures the correct state on
// creation. This method returns true iff the current state is "update".
bool IsCurrentStateUpdate() const;
// Returns true iff the bubble is supposed to show the footer about syncing
// to Google account.
bool ShouldShowFooter() const;
// Returns the ID of the picture to show above the title.
int GetTopIllustration(bool dark_mode) const;
// Returns true if passwords revealing is not locked or re-authentication is
// not available on the given platform. Otherwise, the method schedules
// re-authentication and bubble reopen (the current bubble will be destroyed),
// and returns false immediately. New bubble will reveal the passwords if the
// re-authentication is successful.
bool RevealPasswords();
#if defined(PASSWORD_STORE_SELECT_ENABLED)
// Called by the view when the account store checkbox is toggled.
void OnToggleAccountStore(bool is_checked);
// Returns true iff the password account store is used.
bool IsUsingAccountStore();
#endif // defined(PASSWORD_STORE_SELECT_ENABLED)
password_manager::ui::State state() const { return state_; }
const autofill::PasswordForm& pending_password() const {
return pending_password_;
}
bool are_passwords_revealed_when_bubble_is_opened() const {
return are_passwords_revealed_when_bubble_is_opened_;
}
bool enable_editing() const { return enable_editing_; }
#if defined(UNIT_TEST)
void set_clock(base::Clock* clock) { clock_ = clock; }
void allow_passwords_revealing() {
password_revealing_requires_reauth_ = false;
}
bool password_revealing_requires_reauth() const {
return password_revealing_requires_reauth_;
}
#endif
private:
// PasswordBubbleControllerBase methods:
base::string16 GetTitle() const override;
void ReportInteractions() override;
// URL of the page from where this bubble was triggered.
GURL origin_;
password_manager::ui::State state_;
base::string16 title_;
autofill::PasswordForm pending_password_;
std::vector<autofill::PasswordForm> existing_credentials_;
password_manager::InteractionsStats interaction_stats_;
password_manager::metrics_util::UIDisplayDisposition display_disposition_;
// True iff password revealing should require re-auth for privacy reasons.
bool password_revealing_requires_reauth_;
// True iff bubble should pop up with revealed password value.
bool are_passwords_revealed_when_bubble_is_opened_;
// True iff username/password editing should be enabled.
bool enable_editing_;
// Dismissal reason for a password bubble.
password_manager::metrics_util::UIDismissalReason dismissal_reason_;
// Used to retrieve the current time, in base::Time units.
base::Clock* clock_;
};
#endif // CHROME_BROWSER_UI_PASSWORDS_BUBBLE_CONTROLLERS_SAVE_UPDATE_WITH_ACCOUNT_STORE_BUBBLE_CONTROLLER_H_
...@@ -104,9 +104,8 @@ class ManagePasswordsUIController ...@@ -104,9 +104,8 @@ class ManagePasswordsUIController
return bubble_status_ == BubbleStatus::SHOULD_POP_UP; return bubble_status_ == BubbleStatus::SHOULD_POP_UP;
} }
// TODO(crbug.com/1044034): Rename to GetControllerDelegateProxy after the // virtual to be overridden in tests.
// refactoring of ManagePasswordsBubbleModel is done. virtual base::WeakPtr<PasswordsModelDelegate> GetModelDelegateProxy();
base::WeakPtr<PasswordsModelDelegate> GetModelDelegateProxy();
// PasswordsModelDelegate: // PasswordsModelDelegate:
content::WebContents* GetWebContents() const override; content::WebContents* GetWebContents() const override;
......
...@@ -48,6 +48,8 @@ LocationBarBubbleDelegateView::LocationBarBubbleDelegateView( ...@@ -48,6 +48,8 @@ LocationBarBubbleDelegateView::LocationBarBubbleDelegateView(
// Add observer to close the bubble if the fullscreen state changes. // Add observer to close the bubble if the fullscreen state changes.
if (web_contents) { if (web_contents) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents); Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
// |browser| can be null in tests.
if (browser)
fullscreen_observer_.Add( fullscreen_observer_.Add(
browser->exclusive_access_manager()->fullscreen_controller()); browser->exclusive_access_manager()->fullscreen_controller());
} }
......
// 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_PASSWORDS_PASSWORD_SAVE_UPDATE_WITH_ACCOUNT_STORE_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_PASSWORDS_PASSWORD_SAVE_UPDATE_WITH_ACCOUNT_STORE_VIEW_H_
#include "chrome/browser/ui/passwords/bubble_controllers/save_update_with_account_store_bubble_controller.h"
#include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/editable_combobox/editable_combobox_listener.h"
#include "ui/views/view.h"
namespace views {
class EditableCombobox;
class ToggleImageButton;
#if defined(PASSWORD_STORE_SELECT_ENABLED)
class Checkbox;
#endif // defined(PASSWORD_STORE_SELECT_ENABLED)
} // namespace views
// A view offering the user the ability to save or update credentials (depending
// on |is_update_bubble|) either in the profile and/or account stores. Contains
// a username and password field, and in case of a saving a destination picker.
// In addition, it contains a "Save"/"Update" button and a "Never"/"Nope"
// button.
class PasswordSaveUpdateWithAccountStoreView
: public PasswordBubbleViewBase,
public views::ButtonListener,
public views::EditableComboboxListener {
public:
PasswordSaveUpdateWithAccountStoreView(content::WebContents* web_contents,
views::View* anchor_view,
DisplayReason reason);
private:
~PasswordSaveUpdateWithAccountStoreView() override;
// PasswordBubbleViewBase
PasswordBubbleControllerBase* GetController() override;
const PasswordBubbleControllerBase* GetController() const override;
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// views::EditableComboboxListener:
// Used for both the username and password editable comboboxes.
void OnContentChanged(views::EditableCombobox* editable_combobox) override;
// PasswordBubbleViewBase:
gfx::Size CalculatePreferredSize() const override;
views::View* GetInitiallyFocusedView() override;
bool IsDialogButtonEnabled(ui::DialogButton button) const override;
gfx::ImageSkia GetWindowIcon() override;
bool ShouldShowWindowIcon() const override;
bool ShouldShowCloseButton() const override;
bool Accept() override;
bool Cancel() override;
bool Close() override;
// View:
void AddedToWidget() override;
void OnThemeChanged() override;
void TogglePasswordVisibility();
void UpdateUsernameAndPasswordInModel();
void UpdateDialogButtons();
std::unique_ptr<views::View> CreateFooterView();
SaveUpdateWithAccountStoreBubbleController controller_;
// True iff it is an update password bubble on creation. False iff it is a
// save bubble.
const bool is_update_bubble_;
views::EditableCombobox* username_dropdown_;
views::ToggleImageButton* password_view_button_;
// The view for the password value.
views::EditableCombobox* password_dropdown_;
#if defined(PASSWORD_STORE_SELECT_ENABLED)
views::Checkbox* account_store_checkbox_ = nullptr;
#endif // defined(PASSWORD_STORE_SELECT_ENABLED)
bool are_passwords_revealed_;
};
#endif // CHROME_BROWSER_UI_VIEWS_PASSWORDS_PASSWORD_SAVE_UPDATE_WITH_ACCOUNT_STORE_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/passwords/password_save_update_with_account_store_view.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
#include "chrome/browser/ui/passwords/passwords_model_delegate_mock.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "components/password_manager/core/browser/mock_password_feature_manager.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "content/public/test/web_contents_tester.h"
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
class TestManagePasswordsUIController : public ManagePasswordsUIController {
public:
explicit TestManagePasswordsUIController(content::WebContents* web_contents);
base::WeakPtr<PasswordsModelDelegate> GetModelDelegateProxy() override {
return weak_ptr_factory_.GetWeakPtr();
}
private:
NiceMock<PasswordsModelDelegateMock> model_delegate_mock_;
base::WeakPtrFactory<PasswordsModelDelegate> weak_ptr_factory_;
autofill::PasswordForm pending_password_;
std::vector<std::unique_ptr<autofill::PasswordForm>> current_forms_;
NiceMock<password_manager::MockPasswordFeatureManager> feature_manager_;
};
TestManagePasswordsUIController::TestManagePasswordsUIController(
content::WebContents* web_contents)
: ManagePasswordsUIController(web_contents),
weak_ptr_factory_(&model_delegate_mock_) {
// Do not silently replace an existing ManagePasswordsUIController
// because it unregisters itself in WebContentsDestroyed().
EXPECT_FALSE(web_contents->GetUserData(UserDataKey()));
web_contents->SetUserData(UserDataKey(), base::WrapUnique(this));
ON_CALL(model_delegate_mock_, GetOrigin)
.WillByDefault(ReturnRef(pending_password_.origin));
ON_CALL(model_delegate_mock_, GetState)
.WillByDefault(Return(password_manager::ui::PENDING_PASSWORD_STATE));
ON_CALL(model_delegate_mock_, GetPendingPassword)
.WillByDefault(ReturnRef(pending_password_));
ON_CALL(model_delegate_mock_, GetCurrentForms)
.WillByDefault(ReturnRef(current_forms_));
ON_CALL(model_delegate_mock_, GetWebContents)
.WillByDefault(Return(web_contents));
ON_CALL(feature_manager_, GetDefaultPasswordStore)
.WillByDefault(Return(autofill::PasswordForm::Store::kAccountStore));
}
class PasswordSaveUpdateWithAccountStoreViewTest : public ChromeViewsTestBase {
public:
PasswordSaveUpdateWithAccountStoreViewTest();
~PasswordSaveUpdateWithAccountStoreViewTest() override = default;
void CreateViewAndShow();
void TearDown() override {
view_->GetWidget()->CloseWithReason(
views::Widget::ClosedReason::kCloseButtonClicked);
anchor_widget_.reset();
ChromeViewsTestBase::TearDown();
}
PasswordSaveUpdateWithAccountStoreView* view() { return view_; }
private:
TestingProfile profile_;
std::unique_ptr<content::WebContents> test_web_contents_;
std::unique_ptr<views::Widget> anchor_widget_;
PasswordSaveUpdateWithAccountStoreView* view_;
};
PasswordSaveUpdateWithAccountStoreViewTest::
PasswordSaveUpdateWithAccountStoreViewTest() {
PasswordStoreFactory::GetInstance()->SetTestingFactoryAndUse(
&profile_,
base::BindRepeating(
&password_manager::BuildPasswordStore<
content::BrowserContext,
testing::NiceMock<password_manager::MockPasswordStore>>));
test_web_contents_ =
content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
// Create the test UIController here so that it's bound to
// |test_web_contents_|, and will be retrieved correctly via
// ManagePasswordsUIController::FromWebContents in
// PasswordsModelDelegateFromWebContents().
new TestManagePasswordsUIController(test_web_contents_.get());
}
void PasswordSaveUpdateWithAccountStoreViewTest::CreateViewAndShow() {
// The bubble needs the parent as an anchor.
views::Widget::InitParams params =
CreateParams(views::Widget::InitParams::TYPE_WINDOW);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
anchor_widget_ = std::make_unique<views::Widget>();
anchor_widget_->Init(std::move(params));
anchor_widget_->Show();
view_ = new PasswordSaveUpdateWithAccountStoreView(
test_web_contents_.get(), anchor_widget_->GetContentsView(),
LocationBarBubbleDelegateView::AUTOMATIC);
views::BubbleDialogDelegateView::CreateBubble(view_)->Show();
}
TEST_F(PasswordSaveUpdateWithAccountStoreViewTest, HasTitleAndTwoButtons) {
CreateViewAndShow();
EXPECT_TRUE(view()->ShouldShowWindowTitle());
EXPECT_TRUE(view()->GetOkButton());
EXPECT_TRUE(view()->GetCancelButton());
}
...@@ -4110,6 +4110,7 @@ test("unit_tests") { ...@@ -4110,6 +4110,7 @@ test("unit_tests") {
"../browser/ui/passwords/bubble_controllers/generation_confirmation_bubble_controller_unittest.cc", "../browser/ui/passwords/bubble_controllers/generation_confirmation_bubble_controller_unittest.cc",
"../browser/ui/passwords/bubble_controllers/items_bubble_controller_unittest.cc", "../browser/ui/passwords/bubble_controllers/items_bubble_controller_unittest.cc",
"../browser/ui/passwords/bubble_controllers/save_update_bubble_controller_unittest.cc", "../browser/ui/passwords/bubble_controllers/save_update_bubble_controller_unittest.cc",
"../browser/ui/passwords/bubble_controllers/save_update_with_account_store_bubble_controller_unittest.cc",
"../browser/ui/passwords/bubble_controllers/sign_in_promo_bubble_controller_unittest.cc", "../browser/ui/passwords/bubble_controllers/sign_in_promo_bubble_controller_unittest.cc",
"../browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc", "../browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc",
"../browser/ui/passwords/credential_manager_dialog_controller_impl_unittest.cc", "../browser/ui/passwords/credential_manager_dialog_controller_impl_unittest.cc",
...@@ -4292,6 +4293,7 @@ test("unit_tests") { ...@@ -4292,6 +4293,7 @@ test("unit_tests") {
"../browser/ui/toolbar/media_router_contextual_menu_unittest.cc", "../browser/ui/toolbar/media_router_contextual_menu_unittest.cc",
"../browser/ui/toolbar/mock_media_router_action_controller.cc", "../browser/ui/toolbar/mock_media_router_action_controller.cc",
"../browser/ui/toolbar/mock_media_router_action_controller.h", "../browser/ui/toolbar/mock_media_router_action_controller.h",
"../browser/ui/views/passwords/password_save_update_with_account_store_view_unittest.cc",
"../common/media_router/discovery/media_sink_internal_unittest.cc", "../common/media_router/discovery/media_sink_internal_unittest.cc",
"../common/media_router/discovery/media_sink_service_base_unittest.cc", "../common/media_router/discovery/media_sink_service_base_unittest.cc",
"../common/media_router/mojom/media_router_mojom_traits_unittest.cc", "../common/media_router/mojom/media_router_mojom_traits_unittest.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