Commit a3aaa89b authored by vasilii's avatar vasilii Committed by Commit bot

Don't try to use ManagePasswordsBubbleModel if the password bubble was closed.

The bubble may be closed by some navigation in the content area. However, if the user pressed a button before closing and released after then the button action is still sent. Moreover, all the objects like NSWindow, NSWindowController, NSViewController are still alive. The UI code should not ping ManagePasswordsBubbleModel which is actually destroyed.

BUG=579726

Review URL: https://codereview.chromium.org/1637043002

Cr-Commit-Position: refs/heads/master@{#371513}
parent 7f7eb25a
...@@ -7,12 +7,17 @@ ...@@ -7,12 +7,17 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
class ManagePasswordsBubbleModel;
// Handles user interaction with the content view. // Handles user interaction with the content view.
@protocol ManagePasswordsBubbleContentViewDelegate<NSObject> @protocol ManagePasswordsBubbleContentViewDelegate<NSObject>
// The user performed an action that should dismiss the bubble. // The user performed an action that should dismiss the bubble.
- (void)viewShouldDismiss; - (void)viewShouldDismiss;
// Returns the model object.
@property(nonatomic, readonly) ManagePasswordsBubbleModel* model;
@end @end
// Base class for a state of the password management bubble. // Base class for a state of the password management bubble.
......
...@@ -44,10 +44,8 @@ class ManagePasswordsControllerTest : public CocoaProfileTest { ...@@ -44,10 +44,8 @@ class ManagePasswordsControllerTest : public CocoaProfileTest {
// Helper delegate for testing the views of the password management bubble. // Helper delegate for testing the views of the password management bubble.
@interface ContentViewDelegateMock @interface ContentViewDelegateMock
: NSObject<ManagePasswordsBubbleContentViewDelegate> { : NSObject<ManagePasswordsBubbleContentViewDelegate>
@private @property(nonatomic) ManagePasswordsBubbleModel* model;
BOOL _dismissed;
}
@property(readonly, nonatomic) BOOL dismissed; @property(readonly, nonatomic) BOOL dismissed;
@end @end
......
...@@ -110,6 +110,7 @@ ManagePasswordsControllerTest::GetDisplayReason() const { ...@@ -110,6 +110,7 @@ ManagePasswordsControllerTest::GetDisplayReason() const {
@implementation ContentViewDelegateMock @implementation ContentViewDelegateMock
@synthesize model = _model;
@synthesize dismissed = _dismissed; @synthesize dismissed = _dismissed;
- (void)viewShouldDismiss { - (void)viewShouldDismiss {
......
...@@ -52,6 +52,8 @@ ...@@ -52,6 +52,8 @@
- (void)close { - (void)close {
[currentController_ bubbleWillDisappear]; [currentController_ bubbleWillDisappear];
// The bubble is about to be closed. It destroys the model.
model_ = nil;
[super close]; [super close];
} }
...@@ -60,13 +62,11 @@ ...@@ -60,13 +62,11 @@
currentController_.reset(); currentController_.reset();
if (model_->state() == password_manager::ui::PENDING_PASSWORD_STATE) { if (model_->state() == password_manager::ui::PENDING_PASSWORD_STATE) {
currentController_.reset([[SavePendingPasswordViewController alloc] currentController_.reset([[SavePendingPasswordViewController alloc]
initWithModel:model_ initWithDelegate:self]);
delegate:self]);
} else if (model_->state() == } else if (model_->state() ==
password_manager::ui::PENDING_PASSWORD_UPDATE_STATE) { password_manager::ui::PENDING_PASSWORD_UPDATE_STATE) {
currentController_.reset([[UpdatePendingPasswordViewController alloc] currentController_.reset([[UpdatePendingPasswordViewController alloc]
initWithModel:model_ initWithDelegate:self]);
delegate:self]);
} else if (model_->state() == password_manager::ui::CONFIRMATION_STATE) { } else if (model_->state() == password_manager::ui::CONFIRMATION_STATE) {
currentController_.reset( currentController_.reset(
[[ManagePasswordsBubbleConfirmationViewController alloc] [[ManagePasswordsBubbleConfirmationViewController alloc]
...@@ -148,6 +148,10 @@ ...@@ -148,6 +148,10 @@
[self close]; [self close];
} }
- (ManagePasswordsBubbleModel*)model {
return model_;
}
@end @end
@implementation ManagePasswordsBubbleController (Testing) @implementation ManagePasswordsBubbleController (Testing)
......
...@@ -81,4 +81,11 @@ TEST_F(ManagePasswordsBubbleControllerTest, ...@@ -81,4 +81,11 @@ TEST_F(ManagePasswordsBubbleControllerTest,
[[controller() currentController] class]); [[controller() currentController] class]);
} }
TEST_F(ManagePasswordsBubbleControllerTest, ClearModelOnClose) {
SetUpUpdatePendingState(false);
EXPECT_TRUE(controller().model);
[controller() close];
EXPECT_FALSE(controller().model);
}
} // namespace } // namespace
...@@ -10,18 +10,14 @@ ...@@ -10,18 +10,14 @@
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#import "chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h" #import "chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h"
class ManagePasswordsBubbleModel;
@class PasswordsListViewController; @class PasswordsListViewController;
// Base class for the views that offer to save/update the user's password. // Base class for the views that offer to save/update the user's password.
@interface PendingPasswordViewController @interface PendingPasswordViewController
: ManagePasswordsBubbleContentViewController<NSTextViewDelegate> { : ManagePasswordsBubbleContentViewController<NSTextViewDelegate> {
@private @private
ManagePasswordsBubbleModel* model_; // weak
base::scoped_nsobject<NSButton> closeButton_; base::scoped_nsobject<NSButton> closeButton_;
} }
- (id)initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate;
// Creates a controller for showing username/password and returns its view. // Creates a controller for showing username/password and returns its view.
- (NSView*)createPasswordView; - (NSView*)createPasswordView;
......
...@@ -22,18 +22,12 @@ const SkColor kWarmWelcomeColor = SkColorSetRGB(0x64, 0x64, 0x64); ...@@ -22,18 +22,12 @@ const SkColor kWarmWelcomeColor = SkColorSetRGB(0x64, 0x64, 0x64);
@implementation PendingPasswordViewController @implementation PendingPasswordViewController
- (id)initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
if (([super initWithDelegate:delegate])) {
model_ = model;
}
return self;
}
- (BOOL)textView:(NSTextView*)textView - (BOOL)textView:(NSTextView*)textView
clickedOnLink:(id)link clickedOnLink:(id)link
atIndex:(NSUInteger)charIndex { atIndex:(NSUInteger)charIndex {
model_->OnBrandLinkClicked(); ManagePasswordsBubbleModel* model = [self model];
if (model)
model->OnBrandLinkClicked();
[delegate_ viewShouldDismiss]; [delegate_ viewShouldDismiss];
return YES; return YES;
} }
...@@ -90,8 +84,9 @@ const SkColor kWarmWelcomeColor = SkColorSetRGB(0x64, 0x64, 0x64); ...@@ -90,8 +84,9 @@ const SkColor kWarmWelcomeColor = SkColorSetRGB(0x64, 0x64, 0x64);
[view addSubview:closeButton_]; [view addSubview:closeButton_];
// Title. // Title.
ManagePasswordsBubbleModel* model = [self model];
HyperlinkTextView* titleView = TitleLabelWithLink( HyperlinkTextView* titleView = TitleLabelWithLink(
model_->title(), model_->title_brand_link_range(), self); model->title(), model->title_brand_link_range(), self);
// Force the text to wrap to fit in the bubble size. // Force the text to wrap to fit in the bubble size.
int titleRightPadding = int titleRightPadding =
...@@ -177,7 +172,7 @@ const SkColor kWarmWelcomeColor = SkColorSetRGB(0x64, 0x64, 0x64); ...@@ -177,7 +172,7 @@ const SkColor kWarmWelcomeColor = SkColorSetRGB(0x64, 0x64, 0x64);
} }
- (ManagePasswordsBubbleModel*)model { - (ManagePasswordsBubbleModel*)model {
return model_; return [delegate_ model];
} }
@end @end
......
...@@ -18,9 +18,7 @@ class ManagePasswordsBubbleModel; ...@@ -18,9 +18,7 @@ class ManagePasswordsBubbleModel;
base::scoped_nsobject<NSButton> neverButton_; base::scoped_nsobject<NSButton> neverButton_;
base::scoped_nsobject<PasswordsListViewController> passwordItem_; base::scoped_nsobject<PasswordsListViewController> passwordItem_;
} }
- (SavePendingPasswordViewController*)
initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate;
- (NSView*)createPasswordView; - (NSView*)createPasswordView;
- (NSArray*)createButtonsAndAddThemToView:(NSView*)view; - (NSArray*)createButtonsAndAddThemToView:(NSView*)view;
@end @end
......
...@@ -16,25 +16,21 @@ ...@@ -16,25 +16,21 @@
@implementation SavePendingPasswordViewController @implementation SavePendingPasswordViewController
- (SavePendingPasswordViewController*)
initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
self = [super initWithModel:model
delegate:delegate];
return self;
}
- (NSButton*)defaultButton { - (NSButton*)defaultButton {
return saveButton_; return saveButton_;
} }
- (void)onSaveClicked:(id)sender { - (void)onSaveClicked:(id)sender {
self.model->OnSaveClicked(); ManagePasswordsBubbleModel* model = self.model;
if (model)
model->OnSaveClicked();
[delegate_ viewShouldDismiss]; [delegate_ viewShouldDismiss];
} }
- (void)onNeverForThisSiteClicked:(id)sender { - (void)onNeverForThisSiteClicked:(id)sender {
self.model->OnNeverForThisSiteClicked(); ManagePasswordsBubbleModel* model = self.model;
if (model)
model->OnNeverForThisSiteClicked();
[delegate_ viewShouldDismiss]; [delegate_ viewShouldDismiss];
} }
......
...@@ -32,10 +32,10 @@ class SavePendingPasswordViewControllerTest ...@@ -32,10 +32,10 @@ class SavePendingPasswordViewControllerTest
SavePendingPasswordViewController* controller() { SavePendingPasswordViewController* controller() {
if (!controller_) { if (!controller_) {
[delegate() setModel:GetModelAndCreateIfNull()];
controller_.reset([[SavePendingPasswordViewController alloc] controller_.reset([[SavePendingPasswordViewController alloc]
initWithModel:GetModelAndCreateIfNull() initWithDelegate:delegate()]);
delegate:delegate()]); [controller_ view];
[controller_ loadView];
} }
return controller_.get(); return controller_.get();
} }
...@@ -86,4 +86,16 @@ TEST_F(SavePendingPasswordViewControllerTest, ...@@ -86,4 +86,16 @@ TEST_F(SavePendingPasswordViewControllerTest,
EXPECT_FALSE([controller() createPasswordView]); EXPECT_FALSE([controller() createPasswordView]);
} }
TEST_F(SavePendingPasswordViewControllerTest, CloseBubbleAndHandleClick) {
// A user may press mouse down, some navigation closes the bubble, mouse up
// still sends the action.
SetUpSavePendingState(false);
EXPECT_CALL(*ui_controller(), SavePassword()).Times(0);
EXPECT_CALL(*ui_controller(), NeverSavePassword()).Times(0);
[controller() bubbleWillDisappear];
[delegate() setModel:nil];
[controller().neverButton performClick:nil];
[controller().saveButton performClick:nil];
}
} // namespace } // namespace
...@@ -21,9 +21,7 @@ class ManagePasswordsBubbleModel; ...@@ -21,9 +21,7 @@ class ManagePasswordsBubbleModel;
base::scoped_nsobject<CredentialsSelectionView> base::scoped_nsobject<CredentialsSelectionView>
passwordWithUsernameSelectionItem_; passwordWithUsernameSelectionItem_;
} }
- (UpdatePendingPasswordViewController*)
initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate;
- (NSView*)createPasswordView; - (NSView*)createPasswordView;
- (NSArray*)createButtonsAndAddThemToView:(NSView*)view; - (NSArray*)createButtonsAndAddThemToView:(NSView*)view;
@end @end
......
...@@ -17,30 +17,28 @@ ...@@ -17,30 +17,28 @@
@implementation UpdatePendingPasswordViewController @implementation UpdatePendingPasswordViewController
- (UpdatePendingPasswordViewController*)
initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
self = [super initWithModel:model delegate:delegate];
return self;
}
- (NSButton*)defaultButton { - (NSButton*)defaultButton {
return updateButton_; return updateButton_;
} }
- (void)onUpdateClicked:(id)sender { - (void)onUpdateClicked:(id)sender {
if (passwordWithUsernameSelectionItem_) { ManagePasswordsBubbleModel* model = [self model];
// Multi account case. if (model) {
self.model->OnUpdateClicked( if (passwordWithUsernameSelectionItem_) {
*[passwordWithUsernameSelectionItem_ getSelectedCredentials]); // Multi account case.
} else { model->OnUpdateClicked(
self.model->OnUpdateClicked(self.model->pending_password()); *[passwordWithUsernameSelectionItem_ getSelectedCredentials]);
} else {
model->OnUpdateClicked(model->pending_password());
}
} }
[delegate_ viewShouldDismiss]; [delegate_ viewShouldDismiss];
} }
- (void)onNopeClicked:(id)sender { - (void)onNopeClicked:(id)sender {
self.model->OnNopeUpdateClicked(); ManagePasswordsBubbleModel* model = [self model];
if (model)
model->OnNopeUpdateClicked();
[delegate_ viewShouldDismiss]; [delegate_ viewShouldDismiss];
} }
......
...@@ -36,10 +36,10 @@ class UpdatePendingPasswordViewControllerTest ...@@ -36,10 +36,10 @@ class UpdatePendingPasswordViewControllerTest
UpdatePendingPasswordViewController* controller() { UpdatePendingPasswordViewController* controller() {
if (!controller_) { if (!controller_) {
[delegate() setModel:GetModelAndCreateIfNull()];
controller_.reset([[UpdatePendingPasswordViewController alloc] controller_.reset([[UpdatePendingPasswordViewController alloc]
initWithModel:GetModelAndCreateIfNull() initWithDelegate:delegate()]);
delegate:delegate()]); [controller_ view];
[controller_ loadView];
} }
return controller_.get(); return controller_.get();
} }
...@@ -92,4 +92,16 @@ TEST_F(UpdatePendingPasswordViewControllerTest, ...@@ -92,4 +92,16 @@ TEST_F(UpdatePendingPasswordViewControllerTest,
[[controller() createPasswordView] class]); [[controller() createPasswordView] class]);
} }
TEST_F(UpdatePendingPasswordViewControllerTest, CloseBubbleAndHandleClick) {
// A user may press mouse down, some navigation closes the bubble, mouse up
// still sends the action.
SetUpUpdatePendingState(false);
EXPECT_CALL(*ui_controller(), UpdatePassword(_)).Times(0);
EXPECT_CALL(*ui_controller(), OnNopeUpdateClicked()).Times(0);
[controller() bubbleWillDisappear];
[delegate() setModel:nil];
[controller().updateButton performClick:nil];
[controller().noButton performClick:nil];
}
} // namespace } // namespace
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