Skip to content

Commit f178cf1

Browse files
FHorschigCommit Bot
authored andcommitted
[Android, Crash] Prevent Accessory trying to send data too early
There is a nullptr exception connected to SendViewItems. This CL guesses a solution to this problem: 1. It's only called if the controller was checked to be null, so it must be in the implementation. 2. The view_ is synchronously constructed at construction time and can outside of tests never be null. 3. It's bound to web_contents_, so this also cannot be null. The only dereference left is the GetFocusedFrame() method which can be null. Therefore, accessing the correct frame moves to the driver-side which is much more appropriate for frame-based actions anyway. Bug: 865447 Change-Id: Ie5ea7aed980cdf53e784e5d918a9d54717ad6ab1 Reviewed-on: https://chromium-review.googlesource.com/1143477 Reviewed-by: Vasilii Sukhanov <[email protected]> Commit-Queue: Friedrich Horschig <[email protected]> Cr-Commit-Position: refs/heads/master@{#577496}
1 parent a0d639a commit f178cf1

File tree

4 files changed

+94
-41
lines changed

4 files changed

+94
-41
lines changed

chrome/browser/password_manager/chrome_password_manager_client.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,10 @@ void ChromePasswordManagerClient::FocusedInputChanged(bool is_fillable,
10841084
PasswordAccessoryController::FromWebContents(web_contents());
10851085
if (!accessory)
10861086
return; // No accessory needs change here.
1087-
accessory->RefreshSuggestionsForField(is_fillable, is_password_field);
1087+
accessory->RefreshSuggestionsForField(
1088+
password_manager_driver_bindings_.GetCurrentTargetFrame()
1089+
->GetLastCommittedOrigin(),
1090+
is_fillable, is_password_field);
10881091
#endif // defined(OS_ANDROID)
10891092
}
10901093

chrome/browser/password_manager/password_accessory_controller.cc

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#include "base/strings/utf_string_conversions.h"
1010
#include "chrome/browser/android/preferences/preferences_launcher.h"
11-
#include "chrome/browser/password_manager/password_accessory_view_interface.h"
1211
#include "chrome/browser/password_manager/password_generation_dialog_view_interface.h"
1312
#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
1413
#include "chrome/grit/generated_resources.h"
@@ -22,8 +21,6 @@
2221
#include "content/public/browser/web_contents.h"
2322
#include "ui/base/l10n/l10n_util.h"
2423

25-
#include "chrome/browser/android/preferences/preferences_launcher.h"
26-
2724
using autofill::PasswordForm;
2825
using Item = PasswordAccessoryViewInterface::AccessoryItem;
2926

@@ -210,10 +207,15 @@ void PasswordAccessoryController::OnFilledIntoFocusedField(
210207
}
211208

212209
void PasswordAccessoryController::RefreshSuggestionsForField(
210+
const url::Origin& origin,
213211
bool is_fillable,
214212
bool is_password_field) {
215213
// TODO(crbug/853766): Record CTR metric.
216-
SendViewItems(is_password_field);
214+
view_->OnItemsAvailable(
215+
CreateViewItems(origin,
216+
is_fillable ? origin_suggestions_[origin]
217+
: std::vector<SuggestionElementData>(),
218+
is_password_field));
217219
}
218220

219221
gfx::NativeView PasswordAccessoryController::container_view() const {
@@ -224,13 +226,11 @@ gfx::NativeWindow PasswordAccessoryController::native_window() const {
224226
return web_contents_->GetTopLevelNativeWindow();
225227
}
226228

227-
void PasswordAccessoryController::SendViewItems(bool is_password_field) {
228-
DCHECK(view_);
229-
last_focused_field_was_for_passwords_ = is_password_field;
230-
const url::Origin& origin =
231-
web_contents_->GetFocusedFrame()->GetLastCommittedOrigin();
232-
const std::vector<SuggestionElementData>& suggestions =
233-
origin_suggestions_[origin];
229+
// static
230+
std::vector<Item> PasswordAccessoryController::CreateViewItems(
231+
const url::Origin& origin,
232+
const std::vector<SuggestionElementData>& suggestions,
233+
bool is_password_field) {
234234
std::vector<Item> items;
235235
base::string16 passwords_title_str;
236236

@@ -266,7 +266,5 @@ void PasswordAccessoryController::SendViewItems(bool is_password_field) {
266266
IDS_PASSWORD_MANAGER_ACCESSORY_ALL_PASSWORDS_LINK);
267267
items.emplace_back(manage_passwords_title, manage_passwords_title, false,
268268
Item::Type::OPTION);
269-
270-
// Notify the view about all just created elements.
271-
view_->OnItemsAvailable(items);
269+
return items;
272270
}

chrome/browser/password_manager/password_accessory_controller.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "base/macros.h"
1515
#include "base/memory/weak_ptr.h"
1616
#include "base/strings/string16.h"
17+
#include "chrome/browser/password_manager/password_accessory_view_interface.h"
1718
#include "components/autofill/core/common/filling_status.h"
1819
#include "components/autofill/core/common/password_generation_util.h"
1920
#include "content/public/browser/web_contents_user_data.h"
@@ -28,7 +29,6 @@ namespace password_manager {
2829
class PasswordManagerDriver;
2930
} // namespace password_manager
3031

31-
class PasswordAccessoryViewInterface;
3232
class PasswordGenerationDialogViewInterface;
3333

3434
// The controller for the view located below the keyboard accessory.
@@ -91,8 +91,11 @@ class PasswordAccessoryController
9191
void OnFilledIntoFocusedField(autofill::FillingStatus status);
9292

9393
// Makes sure, that all shown suggestions are appropriate for the currently
94-
// focused field.
95-
void RefreshSuggestionsForField(bool is_fillable, bool is_password_field);
94+
// focused field and for fields that lost the focus. If a field lost focus,
95+
// |is_fillable| will be false.
96+
void RefreshSuggestionsForField(const url::Origin& origin,
97+
bool is_fillable,
98+
bool is_password_field);
9699

97100
// The web page view containing the focused field.
98101
gfx::NativeView container_view() const;
@@ -129,10 +132,12 @@ class PasswordAccessoryController
129132
std::unique_ptr<PasswordAccessoryViewInterface> view,
130133
CreateDialogFactory create_dialog_callback);
131134

132-
// Creates the view items based on the |origin_suggestions_| and sends them to
133-
// the view. If |is_password_field| is false, password suggestions won't be
134-
// interactive.
135-
void SendViewItems(bool is_password_field);
135+
// Creates the view items based on the given |suggestions|.
136+
// If |is_password_field| is false, password suggestions won't be interactive.
137+
static std::vector<PasswordAccessoryViewInterface::AccessoryItem>
138+
CreateViewItems(const url::Origin& origin,
139+
const std::vector<SuggestionElementData>& suggestions,
140+
bool is_password_field);
136141

137142
// Contains the last set of credentials by origin.
138143
std::map<url::Origin, std::vector<SuggestionElementData>> origin_suggestions_;

chrome/browser/password_manager/password_accessory_controller_unittest.cc

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,6 @@ class PasswordAccessoryControllerTest : public ChromeRenderViewHostTestHarness {
245245
std::make_unique<StrictMock<MockPasswordAccessoryView>>(),
246246
mock_dialog_factory_.Get());
247247
NavigateAndCommit(GURL("https://example.com"));
248-
FocusWebContentsOnMainFrame();
249248
}
250249

251250
PasswordAccessoryController* controller() {
@@ -287,8 +286,10 @@ TEST_F(PasswordAccessoryControllerTest, TransformsMatchesToSuggestions) {
287286

288287
controller()->SavePasswordsForOrigin({CreateEntry("Ben", "S3cur3").first},
289288
url::Origin::Create(GURL(kExampleSite)));
290-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
291-
/*is_password_field=*/false);
289+
controller()->RefreshSuggestionsForField(
290+
url::Origin::Create(GURL(kExampleSite)),
291+
/*is_fillable=*/true,
292+
/*is_password_field=*/false);
292293
}
293294

294295
TEST_F(PasswordAccessoryControllerTest, HintsToEmptyUserNames) {
@@ -304,8 +305,10 @@ TEST_F(PasswordAccessoryControllerTest, HintsToEmptyUserNames) {
304305

305306
controller()->SavePasswordsForOrigin({CreateEntry("", "S3cur3").first},
306307
url::Origin::Create(GURL(kExampleSite)));
307-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
308-
/*is_password_field=*/false);
308+
controller()->RefreshSuggestionsForField(
309+
url::Origin::Create(GURL(kExampleSite)),
310+
/*is_fillable=*/true,
311+
/*is_password_field=*/false);
309312
}
310313

311314
TEST_F(PasswordAccessoryControllerTest, SortsAlphabeticalDuringTransform) {
@@ -338,8 +341,10 @@ TEST_F(PasswordAccessoryControllerTest, SortsAlphabeticalDuringTransform) {
338341
{CreateEntry("Ben", "S3cur3").first, CreateEntry("Zebra", "M3h").first,
339342
CreateEntry("Alf", "PWD").first, CreateEntry("Cat", "M1@u").first},
340343
url::Origin::Create(GURL(kExampleSite)));
341-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
342-
/*is_password_field=*/false);
344+
controller()->RefreshSuggestionsForField(
345+
url::Origin::Create(GURL(kExampleSite)),
346+
/*is_fillable=*/true,
347+
/*is_password_field=*/false);
343348
}
344349

345350
TEST_F(PasswordAccessoryControllerTest, RepeatsSuggestionsForSameFrame) {
@@ -357,8 +362,10 @@ TEST_F(PasswordAccessoryControllerTest, RepeatsSuggestionsForSameFrame) {
357362
url::Origin::Create(GURL(kExampleSite)));
358363

359364
// Pretend that any input in the same frame was focused.
360-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
361-
/*is_fillable=*/false);
365+
controller()->RefreshSuggestionsForField(
366+
url::Origin::Create(GURL(kExampleSite)),
367+
/*is_fillable=*/true,
368+
/*is_fillable=*/false);
362369
}
363370

364371
TEST_F(PasswordAccessoryControllerTest, ProvidesEmptySuggestionsMessage) {
@@ -370,8 +377,10 @@ TEST_F(PasswordAccessoryControllerTest, ProvidesEmptySuggestionsMessage) {
370377

371378
controller()->SavePasswordsForOrigin({},
372379
url::Origin::Create(GURL(kExampleSite)));
373-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
374-
/*is_password_field=*/false);
380+
controller()->RefreshSuggestionsForField(
381+
url::Origin::Create(GURL(kExampleSite)),
382+
/*is_fillable=*/true,
383+
/*is_password_field=*/false);
375384
}
376385

377386
TEST_F(PasswordAccessoryControllerTest, RelaysAutomaticGenerationAvailable) {
@@ -439,8 +448,10 @@ TEST_F(PasswordAccessoryControllerTest, PasswordFieldChangesSuggestionType) {
439448
// This should result in the non-interactive suggestion expected above.
440449
controller()->SavePasswordsForOrigin({CreateEntry("Ben", "S3cur3").first},
441450
url::Origin::Create(GURL(kExampleSite)));
442-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
443-
/*is_password_field=*/false);
451+
controller()->RefreshSuggestionsForField(
452+
url::Origin::Create(GURL(kExampleSite)),
453+
/*is_fillable=*/true,
454+
/*is_password_field=*/false);
444455

445456
// Pretend that we focus a password field now: By triggering a refresh with
446457
// |is_password_field| set to true, all suggestions should become interactive.
@@ -452,8 +463,10 @@ TEST_F(PasswordAccessoryControllerTest, PasswordFieldChangesSuggestionType) {
452463
MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"),
453464
true, ItemType::SUGGESTION),
454465
IsDivider(), MatchesOption(manage_passwords_str()))));
455-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
456-
/*is_password_field=*/true);
466+
controller()->RefreshSuggestionsForField(
467+
url::Origin::Create(GURL(kExampleSite)),
468+
/*is_fillable=*/true,
469+
/*is_password_field=*/true);
457470
}
458471

459472
TEST_F(PasswordAccessoryControllerTest, CachesIsReplacedByNewPasswords) {
@@ -467,8 +480,10 @@ TEST_F(PasswordAccessoryControllerTest, CachesIsReplacedByNewPasswords) {
467480
IsDivider(), MatchesOption(manage_passwords_str()))));
468481
controller()->SavePasswordsForOrigin({CreateEntry("Ben", "S3cur3").first},
469482
url::Origin::Create(GURL(kExampleSite)));
470-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
471-
/*is_password_field=*/false);
483+
controller()->RefreshSuggestionsForField(
484+
url::Origin::Create(GURL(kExampleSite)),
485+
/*is_fillable=*/true,
486+
/*is_password_field=*/false);
472487

473488
EXPECT_CALL(*view(),
474489
OnItemsAvailable(ElementsAre(
@@ -480,6 +495,38 @@ TEST_F(PasswordAccessoryControllerTest, CachesIsReplacedByNewPasswords) {
480495
IsDivider(), MatchesOption(manage_passwords_str()))));
481496
controller()->SavePasswordsForOrigin({CreateEntry("Alf", "M3lm4k").first},
482497
url::Origin::Create(GURL(kExampleSite)));
483-
controller()->RefreshSuggestionsForField(/*is_fillable=*/true,
484-
/*is_password_field=*/false);
498+
controller()->RefreshSuggestionsForField(
499+
url::Origin::Create(GURL(kExampleSite)),
500+
/*is_fillable=*/true,
501+
/*is_password_field=*/false);
502+
}
503+
504+
TEST_F(PasswordAccessoryControllerTest, UnfillableFieldClearsSuggestions) {
505+
EXPECT_CALL(*view(),
506+
OnItemsAvailable(ElementsAre(
507+
MatchesLabel(passwords_title_str(kExampleDomain)),
508+
MatchesItem(ASCIIToUTF16("Ben"), ASCIIToUTF16("Ben"), false,
509+
ItemType::SUGGESTION),
510+
MatchesItem(ASCIIToUTF16("S3cur3"), password_for_str("Ben"),
511+
true, ItemType::NON_INTERACTIVE_SUGGESTION),
512+
IsDivider(), MatchesOption(manage_passwords_str()))));
513+
// Set any, non-empty password list and pretend a username field was focused.
514+
// This should result in the non-emtpy suggestions expected above.
515+
controller()->SavePasswordsForOrigin({CreateEntry("Ben", "S3cur3").first},
516+
url::Origin::Create(GURL(kExampleSite)));
517+
controller()->RefreshSuggestionsForField(
518+
url::Origin::Create(GURL(kExampleSite)),
519+
/*is_fillable=*/true,
520+
/*is_password_field=*/false);
521+
522+
// Pretend that the focus was lost or moved to an unfillable field. Now, only
523+
// the empty state message should be sent.
524+
EXPECT_CALL(*view(),
525+
OnItemsAvailable(ElementsAre(
526+
MatchesLabel(passwords_empty_str(kExampleDomain)),
527+
IsDivider(), MatchesOption(manage_passwords_str()))));
528+
controller()->RefreshSuggestionsForField(
529+
url::Origin::Create(GURL(kExampleSite)),
530+
/*is_fillable=*/false,
531+
/*is_password_field=*/false); // Unused.
485532
}

0 commit comments

Comments
 (0)