Commit ebe803ac authored by Peter Boström's avatar Peter Boström Committed by Commit Bot

Separate the manage-passwords dialog.

Splits up some globals and makes this a stand-alone dialog instead of a
child view to a catch-all password dialog. Uses a 6:4 ratio for
username/password rows. These ratios are always fixed and will not vary
based on content. This keeps the look consistent as the contents inside
the dialog changes.

Also fixes regression where additional padding is observed on the
right-hand side of the manage-passwords dialog.

TBR=vasilii@chromium.org

Bug: chromium:654115, chromium:784754
Change-Id: Ic3bc8cee08be90e0f4973fb6752eae355ecd0bb1
Reviewed-on: https://chromium-review.googlesource.com/724126
Commit-Queue: Peter Boström <pbos@chromium.org>
Reviewed-by: default avatarTrent Apted <tapted@chromium.org>
Reviewed-by: default avatarBret Sepulveda <bsep@chromium.org>
Cr-Commit-Position: refs/heads/master@{#523626}
parent a7f4ab11
......@@ -9239,8 +9239,11 @@ Please help our engineers fix this problem. Tell us what happened right before y
<message name="IDS_MANAGE_PASSWORDS_UNDO" desc="The text that is used in the manage passwords bubble when a password is deleted.">
Undo
</message>
<message name="IDS_MANAGE_PASSWORDS_UNDO_TOOLTIP" desc="Tooltip text for restoring a deleted password (undoing removal)">
Restore deleted password for <ph name="USERNAME">$1<ex>user@gmail.com</ex></ph>
</message>
<message name="IDS_MANAGE_PASSWORDS_DELETE" desc="The tooltip of a button that is used in the manage passwords bubble for removing the credential.">
Delete
Delete password for <ph name="USERNAME">$1<ex>user@gmail.com</ex></ph>
</message>
<message name="IDS_MANAGE_PASSWORDS_SHOW_PASSWORD" desc="The tooltip of a button that is used in the manage pending password bubble for revealing the password.">
......
......@@ -2787,6 +2787,8 @@ split_static_library("ui") {
"views/passwords/credentials_selection_view.h",
"views/passwords/manage_password_items_view.cc",
"views/passwords/manage_password_items_view.h",
"views/passwords/manage_passwords_bubble_delegate_view_base.cc",
"views/passwords/manage_passwords_bubble_delegate_view_base.h",
"views/passwords/manage_passwords_bubble_view.cc",
"views/passwords/manage_passwords_bubble_view.h",
"views/payments/contact_info_editor_view_controller.cc",
......
......@@ -181,7 +181,7 @@ NSString* const kDoneTouchBarId = @"DONE";
- (void)onManageClicked:(id)sender {
if (self.model)
self.model->OnManageLinkClicked();
self.model->OnManageClicked();
[self.delegate viewShouldDismiss];
}
......
......@@ -13,7 +13,7 @@
#import "chrome/browser/ui/cocoa/bubble_anchor_helper_views.h"
#import "chrome/browser/ui/cocoa/passwords/passwords_bubble_controller.h"
#include "chrome/browser/ui/views/collected_cookies_views.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_delegate_view_base.h"
#include "chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h"
#include "content/public/browser/web_contents.h"
#import "ui/base/cocoa/cocoa_base_utils.h"
......@@ -64,7 +64,7 @@ void TabDialogsViewsMac::ShowManagePasswordsBubble(bool user_action) {
// earlier because the bubble is shown on mouse release (but dismissed on
// mouse pressed). A Cocoa browser does both on mouse pressed, so a check
// when showing is sufficient.
if (ManagePasswordsBubbleView::manage_password_bubble())
if (ManagePasswordsBubbleDelegateViewBase::manage_password_bubble())
return;
Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
......@@ -79,8 +79,10 @@ void TabDialogsViewsMac::ShowManagePasswordsBubble(bool user_action) {
LocationBarBubbleDelegateView::DisplayReason reason =
user_action ? LocationBarBubbleDelegateView::USER_GESTURE
: LocationBarBubbleDelegateView::AUTOMATIC;
ManagePasswordsBubbleView* bubble_view = new ManagePasswordsBubbleView(
web_contents(), nullptr, anchor_point, reason);
ManagePasswordsBubbleDelegateViewBase* bubble_view =
ManagePasswordsBubbleDelegateViewBase::CreateBubble(
web_contents(), nullptr, anchor_point, reason);
bubble_view->set_arrow(views::BubbleBorder::TOP_RIGHT);
bubble_view->set_parent_window(parent);
views::BubbleDialogDelegateView::CreateBubble(bubble_view);
......@@ -94,10 +96,11 @@ void TabDialogsViewsMac::HideManagePasswordsBubble() {
TabDialogsCocoa::HideManagePasswordsBubble();
return;
}
if (!ManagePasswordsBubbleView::manage_password_bubble())
if (!ManagePasswordsBubbleDelegateViewBase::manage_password_bubble())
return;
content::WebContents* bubble_web_contents =
ManagePasswordsBubbleView::manage_password_bubble()->web_contents();
const content::WebContents* bubble_web_contents =
ManagePasswordsBubbleDelegateViewBase::manage_password_bubble()
->GetWebContents();
if (web_contents() == bubble_web_contents)
ManagePasswordsBubbleView::CloseCurrentBubble();
ManagePasswordsBubbleDelegateViewBase::CloseCurrentBubble();
}
......@@ -449,7 +449,7 @@ void ManagePasswordsBubbleModel::OnOKClicked() {
interaction_keeper_->set_dismissal_reason(metrics_util::CLICKED_OK);
}
void ManagePasswordsBubbleModel::OnManageLinkClicked() {
void ManagePasswordsBubbleModel::OnManageClicked() {
interaction_keeper_->set_dismissal_reason(metrics_util::CLICKED_MANAGE);
if (delegate_)
delegate_->NavigateToPasswordManagerSettingsPage();
......
......@@ -67,8 +67,8 @@ class ManagePasswordsBubbleModel {
// Called by the view code when the "OK" button is clicked by the user.
void OnOKClicked();
// Called by the view code when the manage link is clicked by the user.
void OnManageLinkClicked();
// Called by the view code when the manage button is clicked by the user.
void OnManageClicked();
// Called by the view code when the navigate to passwords.google.com link is
// clicked by the user.
......
......@@ -302,7 +302,7 @@ TEST_F(ManagePasswordsBubbleModelTest, ClickManage) {
PretendManagingPasswords();
EXPECT_CALL(*controller(), NavigateToPasswordManagerSettingsPage());
model()->OnManageLinkClicked();
model()->OnManageClicked();
EXPECT_EQ(password_manager::ui::MANAGE_STATE, model()->state());
DestroyModelExpectReason(password_manager::metrics_util::CLICKED_MANAGE);
......@@ -492,7 +492,7 @@ class ManagePasswordsBubbleModelManageLinkTest
: public ManagePasswordsBubbleModelTest,
public ::testing::WithParamInterface<TestSyncService::SyncedTypes> {};
TEST_P(ManagePasswordsBubbleModelManageLinkTest, OnManageLinkClicked) {
TEST_P(ManagePasswordsBubbleModelManageLinkTest, OnManageClicked) {
TestSyncService* sync_service = static_cast<TestSyncService*>(
ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), &TestingSyncFactoryFunction));
......@@ -502,7 +502,7 @@ TEST_P(ManagePasswordsBubbleModelManageLinkTest, OnManageLinkClicked) {
EXPECT_CALL(*controller(), NavigateToPasswordManagerSettingsPage());
model()->OnManageLinkClicked();
model()->OnManageClicked();
}
INSTANTIATE_TEST_CASE_P(Default,
......
......@@ -26,18 +26,23 @@
namespace {
constexpr char kTestOrigin[] = "https://www.example.com";
constexpr char kTestUsername[] = "test_username";
constexpr char kTestPassword[] = "test_password";
} // namespace
ManagePasswordsTest::ManagePasswordsTest() {
fetcher_.Fetch();
// Populate |test_form_| with some dummy data.
test_form_.signon_realm = kTestOrigin;
test_form_.origin = GURL(kTestOrigin);
test_form_.username_value = base::ASCIIToUTF16(kTestUsername);
test_form_.password_value = base::ASCIIToUTF16(kTestPassword);
password_form_.signon_realm = kTestOrigin;
password_form_.origin = GURL(kTestOrigin);
password_form_.username_value = base::ASCIIToUTF16("test_username");
password_form_.password_value = base::ASCIIToUTF16("test_password");
federated_form_.signon_realm =
"federation://example.com/somelongeroriginurl.com";
federated_form_.origin = GURL(kTestOrigin);
federated_form_.federation_origin =
url::Origin::Create(GURL("https://somelongeroriginurl.com/"));
federated_form_.username_value =
base::ASCIIToUTF16("test_federation_username");
}
ManagePasswordsTest::~ManagePasswordsTest() {
......@@ -61,9 +66,10 @@ void ManagePasswordsTest::ExecuteManagePasswordsCommand() {
void ManagePasswordsTest::SetupManagingPasswords() {
std::map<base::string16, const autofill::PasswordForm*> map;
map.insert(std::make_pair(base::ASCIIToUTF16(kTestUsername), test_form()));
GetController()->OnPasswordAutofilled(map, map.begin()->second->origin,
nullptr);
for (auto* form : {&password_form_, &federated_form_}) {
map.insert(std::make_pair(form->username_value, form));
GetController()->OnPasswordAutofilled(map, form->origin, nullptr);
}
}
void ManagePasswordsTest::SetupPendingPassword() {
......
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_PASSWORDS_MANAGE_PASSWORDS_TEST_H_
#include <memory>
#include <vector>
#include "base/macros.h"
#include "base/metrics/histogram_samples.h"
......@@ -49,7 +50,7 @@ class ManagePasswordsTest : public InProcessBrowserTest {
// Get samples for |histogram|.
std::unique_ptr<base::HistogramSamples> GetSamples(const char* histogram);
autofill::PasswordForm* test_form() { return &test_form_; }
autofill::PasswordForm* test_form() { return &password_form_; }
// Get the UI controller for the current WebContents.
PasswordsClientUIDelegate* GetController();
......@@ -58,7 +59,8 @@ class ManagePasswordsTest : public InProcessBrowserTest {
void(const password_manager::CredentialInfo&));
private:
autofill::PasswordForm test_form_;
autofill::PasswordForm password_form_;
autofill::PasswordForm federated_form_;
base::HistogramTester histogram_tester_;
password_manager::StubPasswordManagerClient client_;
password_manager::StubPasswordManagerDriver driver_;
......
......@@ -33,9 +33,6 @@ constexpr int kAvatarImageSize = 32;
// account chooser.
constexpr int kInfoIconSize = 16;
// The minimal width of the username column.
constexpr int kMinUsernameWidth = 100;
// Crops and scales |image_skia| to the desired size for an account avatar.
gfx::ImageSkia ScaleImageForAccountAvatar(gfx::ImageSkia image_skia);
......
......@@ -219,7 +219,7 @@ views::View* CardUnmaskPromptViews::CreateFootnoteView() {
gfx::Size CardUnmaskPromptViews::CalculatePreferredSize() const {
const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_WIDTH_CONTAINING_MULTILINE_TEXT);
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
return gfx::Size(width, GetHeightForWidth(width));
}
......
......@@ -227,7 +227,7 @@ bool ExtensionUninstallDialogDelegateView::Cancel() {
gfx::Size ExtensionUninstallDialogDelegateView::CalculatePreferredSize() const {
const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_WIDTH_CONTAINING_MULTILINE_TEXT);
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
return gfx::Size(width, GetHeightForWidth(width));
}
......
......@@ -50,7 +50,7 @@ PWAConfirmationView::~PWAConfirmationView() {}
gfx::Size PWAConfirmationView::CalculatePreferredSize() const {
int bubble_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_WIDTH_CONTAINING_MULTILINE_TEXT);
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
gfx::Size size = views::DialogDelegateView::CalculatePreferredSize();
size.SetToMin(gfx::Size(bubble_width - margins().width(), size.height()));
......
......@@ -82,8 +82,10 @@ int ChromeLayoutProvider::GetDistanceMetric(int metric) const {
return 8;
case DISTANCE_TOAST_LABEL_VERTICAL:
return 12;
case DISTANCE_MODAL_DIALOG_WIDTH_CONTAINING_MULTILINE_TEXT:
case DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH:
return 400;
case DISTANCE_BUBBLE_PREFERRED_WIDTH:
return 320;
default:
return views::LayoutProvider::GetDistanceMetric(metric);
}
......
......@@ -51,13 +51,12 @@ enum ChromeDistanceMetric {
DISTANCE_UNRELATED_CONTROL_HORIZONTAL_LARGE,
// Larger vertical spacing between unrelated controls.
DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE,
// Minimum size for modal dialogs. Used when multiline text won't wrap without
// a small container (because its preferred size defaults to the full
// unwrapped text length).
// TODO(pbos): Remove need for this hack by adding a reasonable text-wrap
// default preferred width (ideally smaller than this size). Also add a way of
// enforcing different snap points for modal dialogs (exclude 320px).
DISTANCE_MODAL_DIALOG_WIDTH_CONTAINING_MULTILINE_TEXT,
// Width of modal dialogs unless the content is too wide to make that
// feasible.
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH,
// Width of a bubble unless the content is too wide to make that
// feasible.
DISTANCE_BUBBLE_PREFERRED_WIDTH,
};
class ChromeLayoutProvider : public views::LayoutProvider {
......
......@@ -102,7 +102,9 @@ int HarmonyLayoutProvider::GetDistanceMetric(int metric) const {
return kHarmonyLayoutUnit;
case DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE:
return kHarmonyLayoutUnit;
case DISTANCE_MODAL_DIALOG_WIDTH_CONTAINING_MULTILINE_TEXT:
case DISTANCE_BUBBLE_PREFERRED_WIDTH:
return kSmallSnapPoint;
case DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH:
return kMediumSnapPoint;
default:
return ChromeLayoutProvider::GetDistanceMetric(metric);
......
......@@ -65,7 +65,7 @@ ImportLockDialogView::~ImportLockDialogView() {
gfx::Size ImportLockDialogView::CalculatePreferredSize() const {
const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_WIDTH_CONTAINING_MULTILINE_TEXT);
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
return gfx::Size(width, GetHeightForWidth(width));
}
......
......@@ -108,10 +108,6 @@ void LocationBarBubbleDelegateView::Observe(
CloseBubble();
}
void LocationBarBubbleDelegateView::CloseBubble() {
GetWidget()->Close();
}
void LocationBarBubbleDelegateView::AdjustForFullscreen(
const gfx::Rect& screen_bounds) {
if (GetAnchorView())
......@@ -124,3 +120,7 @@ void LocationBarBubbleDelegateView::AdjustForFullscreen(
: (screen_bounds.right() - horizontal_offset);
SetAnchorRect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0));
}
void LocationBarBubbleDelegateView::CloseBubble() {
GetWidget()->Close();
}
......@@ -55,6 +55,12 @@ class LocationBarBubbleDelegateView : public views::BubbleDialogDelegateView,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// If the bubble is not anchored to a view, places the bubble in the top right
// (left in RTL) of the |screen_bounds| that contain web contents's browser
// window. Because the positioning is based on the size of the bubble, this
// must be called after the bubble is created.
void AdjustForFullscreen(const gfx::Rect& screen_bounds);
protected:
// The class listens for WebContentsView events and closes the bubble. Useful
// for bubbles that do not start out focused but need to close when the user
......@@ -80,12 +86,6 @@ class LocationBarBubbleDelegateView : public views::BubbleDialogDelegateView,
// Closes the bubble.
virtual void CloseBubble();
// If the bubble is not anchored to a view, places the bubble in the top right
// (left in RTL) of the |screen_bounds| that contain web contents's browser
// window. Because the positioning is based on the size of the bubble, this
// must be called after the bubble is created.
void AdjustForFullscreen(const gfx::Rect& screen_bounds);
private:
// Used to register for fullscreen change notifications.
content::NotificationRegistrar registrar_;
......
......@@ -9,7 +9,10 @@
#include <vector>
#include "base/macros.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_delegate_view_base.h"
#include "components/autofill/core/common/password_form.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"
namespace views {
......@@ -17,8 +20,6 @@ class Textfield;
class Label;
} // namespace views
class ManagePasswordsBubbleModel;
// Standalone functions for creating username and password views.
std::unique_ptr<views::Label> CreateUsernameLabel(
const autofill::PasswordForm& form);
......@@ -28,28 +29,36 @@ std::unique_ptr<views::Label> CreatePasswordLabel(
std::unique_ptr<views::Textfield> CreateUsernameEditable(
const autofill::PasswordForm& form);
// A custom view of individual credentials. The view is represented as a table
// where each row can offer the user the ability to undo a deletion action.
class ManagePasswordItemsView : public views::View {
// A dialog for managing stored password and federated login information for a
// specific site. A user can remove managed credentials for the site via this
// dialog.
class ManagePasswordItemsView : public ManagePasswordsBubbleDelegateViewBase,
public views::ButtonListener {
public:
ManagePasswordItemsView(
ManagePasswordsBubbleModel* manage_passwords_bubble_model,
const std::vector<autofill::PasswordForm>* password_forms);
ManagePasswordItemsView(content::WebContents* web_contents,
views::View* anchor_view,
const gfx::Point& anchor_point,
DisplayReason reason);
~ManagePasswordItemsView() override;
private:
class PasswordFormRow;
class PasswordRow;
~ManagePasswordItemsView() override;
void NotifyPasswordFormAction(
const autofill::PasswordForm& password_form,
ManagePasswordsBubbleModel::PasswordAction action);
void RecreateLayout();
void AddRows();
void NotifyPasswordFormStatusChanged(
const autofill::PasswordForm& password_form, bool deleted);
// LocationBarBubbleDelegateView:
View* CreateExtraView() override;
int GetDialogButtons() const override;
bool ShouldShowCloseButton() const override;
gfx::Size CalculatePreferredSize() const override;
// Changes the views according to the state of |password_forms_rows_|.
void Refresh();
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
std::vector<std::unique_ptr<PasswordFormRow>> password_forms_rows_;
ManagePasswordsBubbleModel* model_;
std::vector<std::unique_ptr<PasswordRow>> password_rows_;
DISALLOW_COPY_AND_ASSIGN(ManagePasswordItemsView);
};
......
// Copyright 2017 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/manage_passwords_bubble_delegate_view_base.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/passwords/passwords_model_delegate.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/passwords/manage_password_items_view.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
#include "ui/base/material_design/material_design_controller.h"
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
#include "chrome/browser/ui/views/frame/browser_view.h"
#endif
// static
ManagePasswordsBubbleDelegateViewBase*
ManagePasswordsBubbleDelegateViewBase::g_manage_passwords_bubble_ = nullptr;
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
// static
void ManagePasswordsBubbleDelegateViewBase::ShowBubble(
content::WebContents* web_contents,
DisplayReason reason) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
DCHECK(browser);
DCHECK(browser->window());
DCHECK(!g_manage_passwords_bubble_ ||
!g_manage_passwords_bubble_->GetWidget()->IsVisible());
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
bool is_fullscreen = browser_view->IsFullscreen();
views::View* anchor_view = nullptr;
if (!is_fullscreen) {
if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
anchor_view = browser_view->GetLocationBarView();
} else {
anchor_view =
browser_view->GetLocationBarView()->manage_passwords_icon_view();
}
}
ManagePasswordsBubbleDelegateViewBase* bubble =
CreateBubble(web_contents, anchor_view, gfx::Point(), reason);
DCHECK(bubble);
DCHECK(bubble == g_manage_passwords_bubble_);
if (is_fullscreen)
g_manage_passwords_bubble_->set_parent_window(
web_contents->GetNativeView());
views::Widget* bubble_widget =
views::BubbleDialogDelegateView::CreateBubble(g_manage_passwords_bubble_);
if (anchor_view) {
bubble_widget->AddObserver(
browser_view->GetLocationBarView()->manage_passwords_icon_view());
}
// Adjust for fullscreen after creation as it relies on the content size.
if (is_fullscreen) {
g_manage_passwords_bubble_->AdjustForFullscreen(
browser_view->GetBoundsInScreen());
}
g_manage_passwords_bubble_->ShowForReason(reason);
}
#endif // !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
// static
ManagePasswordsBubbleDelegateViewBase*
ManagePasswordsBubbleDelegateViewBase::CreateBubble(
content::WebContents* web_contents,
views::View* anchor_view,
const gfx::Point& anchor_point,
DisplayReason reason) {
ManagePasswordsBubbleDelegateViewBase* view;
password_manager::ui::State model_state =
PasswordsModelDelegateFromWebContents(web_contents)->GetState();
if (model_state == password_manager::ui::MANAGE_STATE) {
view = new ManagePasswordItemsView(web_contents, anchor_view, anchor_point,
reason);
} else {
// TODO(crbug.com/654115): Get rid of the one-bubble-for-everything
// BubbleView.
view = new ManagePasswordsBubbleView(web_contents, anchor_view,
anchor_point, reason);
}
g_manage_passwords_bubble_ = view;
return view;
}
// static
void ManagePasswordsBubbleDelegateViewBase::CloseCurrentBubble() {
if (g_manage_passwords_bubble_)
g_manage_passwords_bubble_->GetWidget()->Close();
}
// static
void ManagePasswordsBubbleDelegateViewBase::ActivateBubble() {
DCHECK(g_manage_passwords_bubble_);
DCHECK(g_manage_passwords_bubble_->GetWidget()->IsVisible());
g_manage_passwords_bubble_->GetWidget()->Activate();
}
ManagePasswordsBubbleDelegateViewBase::ManagePasswordsBubbleDelegateViewBase(
content::WebContents* web_contents,
views::View* anchor_view,
const gfx::Point& anchor_point,
DisplayReason reason)
: LocationBarBubbleDelegateView(anchor_view, anchor_point, web_contents),
model_(PasswordsModelDelegateFromWebContents(web_contents),
reason == AUTOMATIC ? ManagePasswordsBubbleModel::AUTOMATIC
: ManagePasswordsBubbleModel::USER_ACTION),
mouse_handler_(
std::make_unique<WebContentMouseHandler>(this,
model_.GetWebContents())) {}
ManagePasswordsBubbleDelegateViewBase::
~ManagePasswordsBubbleDelegateViewBase() {
if (g_manage_passwords_bubble_ == this)
g_manage_passwords_bubble_ = nullptr;
}
const content::WebContents*
ManagePasswordsBubbleDelegateViewBase::GetWebContents() const {
return model_.GetWebContents();
}
void ManagePasswordsBubbleDelegateViewBase::CloseBubble() {
mouse_handler_.reset();
LocationBarBubbleDelegateView::CloseBubble();
}
base::string16 ManagePasswordsBubbleDelegateViewBase::GetWindowTitle() const {
return model_.title();
}
bool ManagePasswordsBubbleDelegateViewBase::ShouldShowWindowTitle() const {
return !model_.title().empty();
}
// Copyright 2017 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_MANAGE_PASSWORDS_BUBBLE_DELEGATE_VIEW_BASE_H_
#define CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_PASSWORDS_BUBBLE_DELEGATE_VIEW_BASE_H_
#include "base/macros.h"
#include "build/build_config.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
#include "ui/base/ui_features.h"
namespace content {
class WebContents;
}
// Base class for all manage-passwords bubbles. Provides static methods for
// creating and showing these dialogs. Also used to access the web contents
// related to the dialog.
// These bubbles remove themselves as globals on destruction.
// TODO(pbos): Remove static global usage and move dialog ownership to
// TabDialog instances. Consider removing access to GetWebContents() through
// this class when ownership has moved to TabDialog instances, as it's hopefully
// no longer relevant for checking dialog ownership. These two work items should
// make this base class significantly smaller.
class ManagePasswordsBubbleDelegateViewBase
: public LocationBarBubbleDelegateView {
public:
// Returns a pointer to the bubble.
static ManagePasswordsBubbleDelegateViewBase* manage_password_bubble() {
return g_manage_passwords_bubble_;
}
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
// Shows an appropriate bubble on the toolkit-views Browser window containing
// |web_contents|.
static void ShowBubble(content::WebContents* web_contents,
DisplayReason reason);
#endif
// Creates and returns the passwords manager bubble UI appropriate for the
// current password_manager::ui::State value for the provided |web_contents|.
static ManagePasswordsBubbleDelegateViewBase* CreateBubble(
content::WebContents* web_contents,
views::View* anchor_view,
const gfx::Point& anchor_point,
DisplayReason reason);
// Closes the existing bubble.
static void CloseCurrentBubble();
// Makes the bubble the foreground window.
static void ActivateBubble();
const content::WebContents* GetWebContents() const;
// LocationBarBubbleDelegateView:
base::string16 GetWindowTitle() const override;
bool ShouldShowWindowTitle() const override;
// These model-accessor methods are public for testing.
ManagePasswordsBubbleModel* model() { return &model_; }
const ManagePasswordsBubbleModel* model() const { return &model_; }
protected:
ManagePasswordsBubbleDelegateViewBase(content::WebContents* web_contents,
views::View* anchor_view,
const gfx::Point& anchor_point,
DisplayReason reason);
~ManagePasswordsBubbleDelegateViewBase() override;
// LocationBarBubbleDelegateView:
void CloseBubble() override;
private:
// Singleton instance of the Password bubble.The instance is owned by the
// Bubble and will be deleted when the bubble closes.
static ManagePasswordsBubbleDelegateViewBase* g_manage_passwords_bubble_;
ManagePasswordsBubbleModel model_;
// Listens for WebContentsView events and closes the bubble so the bubble gets
// dismissed when users keep using the web page.
std::unique_ptr<WebContentMouseHandler> mouse_handler_;
DISALLOW_COPY_AND_ASSIGN(ManagePasswordsBubbleDelegateViewBase);
};
#endif // CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_PASSWORDS_BUBBLE_DELEGATE_VIEW_BASE_H_
......@@ -6,15 +6,9 @@
#define CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_PASSWORDS_BUBBLE_VIEW_H_
#include "base/macros.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
#include "ui/base/ui_features.h"
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_delegate_view_base.h"
#include "ui/views/controls/styled_label_listener.h"
namespace content {
class WebContents;
}
// The ManagePasswordsBubbleView controls the contents of the bubble which
// pops up when Chrome offers to save a user's password, or when the user
// interacts with the Omnibox icon. It has two distinct states:
......@@ -22,36 +16,15 @@ class WebContents;
// 1. PendingView: Offers the user the possibility of saving credentials.
// 2. ManageView: Displays the current page's saved credentials.
// 3. BlacklistedView: Informs the user that the current page is blacklisted.
//
class ManagePasswordsBubbleView : public LocationBarBubbleDelegateView,
class ManagePasswordsBubbleView : public ManagePasswordsBubbleDelegateViewBase,
public views::StyledLabelListener {
public:
static constexpr int kDesiredBubbleWidth = 370;
#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
// Shows the bubble.
static void ShowBubble(content::WebContents* web_contents,
DisplayReason reason);
#endif
// Closes the existing bubble.
static void CloseCurrentBubble();
// Makes the bubble the foreground window.
static void ActivateBubble();
// Returns a pointer to the bubble.
static ManagePasswordsBubbleView* manage_password_bubble() {
return manage_passwords_bubble_;
}
ManagePasswordsBubbleView(content::WebContents* web_contents,
views::View* anchor_view,
const gfx::Point& anchor_point,
DisplayReason reason);
content::WebContents* web_contents() const;
#if defined(UNIT_TEST)
const View* initially_focused_view() const {
return initially_focused_view_;
......@@ -62,11 +35,8 @@ class ManagePasswordsBubbleView : public LocationBarBubbleDelegateView,
}
#endif
ManagePasswordsBubbleModel* model() { return &model_; }
private:
class AutoSigninView;
class ManageView;
class PendingView;
class SaveConfirmationView;
class SignInPromoView;
......@@ -81,10 +51,7 @@ class ManagePasswordsBubbleView : public LocationBarBubbleDelegateView,
int GetDialogButtons() const override;
views::View* GetInitiallyFocusedView() override;
void Init() override;
void CloseBubble() override;
void AddedToWidget() override;
base::string16 GetWindowTitle() const override;
bool ShouldShowWindowTitle() const override;
gfx::ImageSkia GetWindowIcon() override;
bool ShouldShowWindowIcon() const override;
bool ShouldShowCloseButton() const override;
......@@ -108,21 +75,11 @@ class ManagePasswordsBubbleView : public LocationBarBubbleDelegateView,
initially_focused_view_ = view;
}
// Singleton instance of the Password bubble. The Password bubble can only be
// shown on the active browser window, so there is no case in which it will be
// shown twice at the same time. The instance is owned by the Bubble and will
// be deleted when the bubble closes.
static ManagePasswordsBubbleView* manage_passwords_bubble_;
// The timeout in seconds for the auto sign-in toast.
static int auto_signin_toast_timeout_;
ManagePasswordsBubbleModel model_;
views::View* initially_focused_view_;
std::unique_ptr<WebContentMouseHandler> mouse_handler_;
DISALLOW_COPY_AND_ASSIGN(ManagePasswordsBubbleView);
};
......
......@@ -32,6 +32,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_features.h"
#include "ui/views/window/dialog_client_view.h"
using net::test_server::BasicHttpResponse;
using net::test_server::HttpRequest;
......@@ -50,6 +51,14 @@ bool IsBubbleShowing() {
GetWidget()->IsVisible();
}
const views::DialogClientView* GetDialogClientView(
const LocationBarBubbleDelegateView* bubble) {
const views::DialogClientView* view =
bubble->GetWidget()->client_view()->AsDialogClientView();
DCHECK(view);
return view;
}
} // namespace
namespace metrics_util = password_manager::metrics_util;
......@@ -92,7 +101,8 @@ IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, BasicOpenAndClose) {
SetupPendingPassword();
EXPECT_TRUE(IsBubbleShowing());
const ManagePasswordsBubbleView* bubble =
ManagePasswordsBubbleView::manage_password_bubble();
static_cast<const ManagePasswordsBubbleView*>(
ManagePasswordsBubbleView::manage_password_bubble());
EXPECT_TRUE(bubble->initially_focused_view());
EXPECT_FALSE(bubble->GetFocusManager()->GetFocusedView());
ManagePasswordsBubbleView::CloseCurrentBubble();
......@@ -107,7 +117,8 @@ IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, BasicOpenAndClose) {
browser()->tab_strip_model()->GetActiveWebContents())
->ShowManagePasswordsBubble(true /* user_action */);
EXPECT_TRUE(IsBubbleShowing());
bubble = ManagePasswordsBubbleView::manage_password_bubble();
bubble = static_cast<const ManagePasswordsBubbleView*>(
ManagePasswordsBubbleView::manage_password_bubble());
EXPECT_TRUE(bubble->initially_focused_view());
EXPECT_EQ(bubble->initially_focused_view(),
bubble->GetFocusManager()->GetFocusedView());
......@@ -124,10 +135,10 @@ IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, CommandControlsBubble) {
EXPECT_FALSE(IsBubbleShowing());
ExecuteManagePasswordsCommand();
EXPECT_TRUE(IsBubbleShowing());
const ManagePasswordsBubbleView* bubble =
const LocationBarBubbleDelegateView* bubble =
ManagePasswordsBubbleView::manage_password_bubble();
EXPECT_TRUE(bubble->initially_focused_view());
EXPECT_EQ(bubble->initially_focused_view(),
EXPECT_TRUE(GetDialogClientView(bubble)->ok_button());
EXPECT_EQ(GetDialogClientView(bubble)->ok_button(),
bubble->GetFocusManager()->GetFocusedView());
ManagePasswordsBubbleView::CloseCurrentBubble();
EXPECT_FALSE(IsBubbleShowing());
......@@ -323,12 +334,14 @@ IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, TwoTabsWithBubbleClose) {
// events directly to the button, since that's buried in private classes.
// Instead, simulate the action in ManagePasswordsBubbleView::PendingView::
// ButtonPressed(), and simulate the OS event queue by posting a task.
auto press_button = [](ManagePasswordsBubbleView* bubble, bool* ran) {
auto press_button = [](ManagePasswordsBubbleDelegateViewBase* bubble,
bool* ran) {
bubble->model()->OnNeverForThisSiteClicked();
*ran = true;
};
ManagePasswordsBubbleView* bubble =
ManagePasswordsBubbleView::manage_password_bubble();
ManagePasswordsBubbleDelegateViewBase* bubble =
ManagePasswordsBubbleDelegateViewBase::manage_password_bubble();
bool ran_event_task = false;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(press_button, bubble, &ran_event_task));
......
......@@ -68,21 +68,21 @@ void TabDialogsViews::ShowProfileSigninConfirmation(
}
void TabDialogsViews::ShowManagePasswordsBubble(bool user_action) {
if (ManagePasswordsBubbleView::manage_password_bubble()) {
if (ManagePasswordsBubbleDelegateViewBase::manage_password_bubble()) {
// The bubble is currently shown for some other tab. We should close it now
// and open for |web_contents_|.
ManagePasswordsBubbleView::CloseCurrentBubble();
}
ManagePasswordsBubbleView::ShowBubble(
web_contents_, user_action ? ManagePasswordsBubbleView::USER_GESTURE
: ManagePasswordsBubbleView::AUTOMATIC);
web_contents_, user_action ? LocationBarBubbleDelegateView::USER_GESTURE
: LocationBarBubbleDelegateView::AUTOMATIC);
}
void TabDialogsViews::HideManagePasswordsBubble() {
if (!ManagePasswordsBubbleView::manage_password_bubble())
ManagePasswordsBubbleDelegateViewBase* bubble =
ManagePasswordsBubbleDelegateViewBase::manage_password_bubble();
if (!bubble)
return;
content::WebContents* bubble_web_contents =
ManagePasswordsBubbleView::manage_password_bubble()->web_contents();
if (web_contents_ == bubble_web_contents)
if (bubble->GetWebContents() == web_contents_)
ManagePasswordsBubbleView::CloseCurrentBubble();
}
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