Commit 976e7da2 authored by Milica Selakovic's avatar Milica Selakovic Committed by Commit Bot

[Password Manager] Integrate PasswordScriptFetcher to Password Check Manager

This CL adds check for scripts availability only
if kPasswordChageInSettings flag is enabled.
This CL also changes PasswordScriptsFetcherBridge so it does not use
Profile information and can be used in SafetyCheck to prewarm cache.

Bug: 1086114, 1092444
Change-Id: I87021c2e65c7886ad90ee80844b9a2e46fb4b626
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2351994
Commit-Queue: Milica Selakovic <selakovic@google.com>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: default avatarIoana Pandele <ioanap@chromium.org>
Reviewed-by: default avatarAndrey Zaytsev <andzaytsev@google.com>
Reviewed-by: default avatarBoris Sazonov <bsazonov@chromium.org>
Reviewed-by: default avatarDominic Battré <battre@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799596}
parent 06919a97
......@@ -3109,7 +3109,6 @@ generate_jni("chrome_jni_headers") {
"java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationPopupBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordScriptsFetcherBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEditingBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/PasswordUIView.java",
"java/src/org/chromium/chrome/browser/permissions/PermissionSettingsBridge.java",
......
......@@ -1226,7 +1226,6 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogView.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogViewBinder.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordScriptsFetcherBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/CallbackDelayer.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/DialogManager.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/ExportErrorDialogFragment.java",
......
......@@ -60,7 +60,7 @@ public class PasswordManagerLauncher {
if (tryShowingTheGooglePasswordManager(activity)) return;
}
if (ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORD_CHANGE_IN_SETTINGS)) {
PasswordScriptsFetcherBridge.prewarmCache(Profile.getLastUsedRegularProfile());
PasswordScriptsFetcherBridge.prewarmCache();
}
}
......
......@@ -2855,6 +2855,7 @@ static_library("browser") {
"password_manager/android/password_manager_infobar_delegate_android.h",
"password_manager/android/password_manager_launcher_android.cc",
"password_manager/android/password_manager_launcher_android.h",
"password_manager/android/password_scripts_fetcher_android.cc",
"password_manager/android/save_password_infobar_delegate_android.cc",
"password_manager/android/save_password_infobar_delegate_android.h",
"password_manager/android/touch_to_fill_view.h",
......@@ -2968,6 +2969,7 @@ static_library("browser") {
"//chrome/browser/offline_pages/prefetch/notifications",
"//chrome/browser/optimization_guide/android:jni_headers",
"//chrome/browser/password_check/android:jni_headers",
"//chrome/browser/password_manager/android:jni_headers",
"//chrome/browser/payments/android:jni_headers",
"//chrome/browser/policy/android:jni_headers",
"//chrome/browser/privacy:jni_headers",
......
......@@ -103,6 +103,21 @@ class PasswordCheckBridge {
PasswordCheckBridgeJni.get().stopCheck(mNativePasswordCheckBridge);
}
/**
*
* @return Whether the scripts refreshment is finished.
*/
boolean areScriptsRefreshed() {
return PasswordCheckBridgeJni.get().areScriptsRefreshed(mNativePasswordCheckBridge);
}
/**
* Invokes scripts refreshment.
*/
void refreshScripts() {
PasswordCheckBridgeJni.get().refreshScripts(mNativePasswordCheckBridge);
}
/**
* @return The timestamp of the last completed check.
*/
......@@ -163,6 +178,8 @@ class PasswordCheckBridge {
long create(PasswordCheckBridge passwordCheckBridge);
void startCheck(long nativePasswordCheckBridge);
void stopCheck(long nativePasswordCheckBridge);
boolean areScriptsRefreshed(long nativePasswordCheckBridge);
void refreshScripts(long nativePasswordCheckBridge);
long getLastCheckTimestamp(long nativePasswordCheckBridge);
int getCompromisedCredentialsCount(long nativePasswordCheckBridge);
int getSavedPasswordsCount(long nativePasswordCheckBridge);
......
......@@ -37,6 +37,7 @@ class PasswordCheckImpl implements PasswordCheck, PasswordCheckObserver {
fragmentArgs.putInt(
PasswordCheckFragmentView.PASSWORD_CHECK_REFERRER, passwordCheckReferrer);
launcher.launchSettingsActivity(context, PasswordCheckFragmentView.class, fragmentArgs);
mPasswordCheckBridge.refreshScripts();
}
@Override
......@@ -136,4 +137,9 @@ class PasswordCheckImpl implements PasswordCheck, PasswordCheckObserver {
public void stopCheck() {
mPasswordCheckBridge.stopCheck();
}
@Override
public boolean areScriptsRefreshed() {
return mPasswordCheckBridge.areScriptsRefreshed();
}
}
......@@ -66,6 +66,9 @@ class PasswordCheckMediator
@Override
public void onCompromisedCredentialsFetchCompleted() {
if (!getPasswordCheck().areScriptsRefreshed()) {
return;
}
CompromisedCredential[] credentials = getPasswordCheck().getCompromisedCredentials();
assert credentials != null;
ListModel<ListItem> items = mModel.get(ITEMS);
......
......@@ -113,4 +113,9 @@ public interface PasswordCheck extends PasswordCheckComponentUi.Delegate {
* Stops the password check, if one is running. Otherwise, does nothing.
*/
void stopCheck();
/**
* Checks if scripts refreshment is finished.
*/
boolean areScriptsRefreshed();
}
......@@ -193,6 +193,7 @@ public class PasswordCheckControllerTest {
public void testCreatesEntryForExistingCredentials() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA});
when(mPasswordCheck.areScriptsRefreshed()).thenReturn(true);
when(mChangePasswordDelegate.canManuallyChangeCredential(eq(ANA))).thenReturn(true);
mMediator.onPasswordCheckStatusChanged(IDLE);
......@@ -208,6 +209,7 @@ public class PasswordCheckControllerTest {
public void testHidesChangeButtonIfManualChangeIsNotPossible() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {BOB});
when(mPasswordCheck.areScriptsRefreshed()).thenReturn(true);
when(mChangePasswordDelegate.canManuallyChangeCredential(eq(BOB))).thenReturn(false);
mMediator.onPasswordCheckStatusChanged(IDLE);
......@@ -223,6 +225,7 @@ public class PasswordCheckControllerTest {
public void testAppendsEntryForNewlyFoundCredentials() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA});
when(mPasswordCheck.areScriptsRefreshed()).thenReturn(true);
when(mChangePasswordDelegate.canManuallyChangeCredential(eq(BOB))).thenReturn(true);
mMediator.onPasswordCheckStatusChanged(IDLE);
mMediator.onCompromisedCredentialsFetchCompleted();
......@@ -243,6 +246,7 @@ public class PasswordCheckControllerTest {
// First call adds only ANA.
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA});
when(mPasswordCheck.areScriptsRefreshed()).thenReturn(true);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).size(), is(2)); // Header + existing credentials.
......
......@@ -110,6 +110,14 @@ void PasswordCheckBridge::Destroy(JNIEnv* env) {
delete this;
}
bool PasswordCheckBridge::AreScriptsRefreshed(JNIEnv* env) const {
return check_manager_.AreScriptsRefreshed();
}
void PasswordCheckBridge::RefreshScripts(JNIEnv* env) {
check_manager_.RefreshScripts();
}
void PasswordCheckBridge::OnSavedPasswordsFetched(int count) {
Java_PasswordCheckBridge_onSavedPasswordsFetched(
base::android::AttachCurrentThread(), java_bridge_, count);
......
......@@ -58,6 +58,12 @@ class PasswordCheckBridge : public PasswordCheckManager::Observer {
// Called by Java when the bridge is no longer needed. Destructs itself.
void Destroy(JNIEnv* env);
// Checks if script refreshment is finished.
bool AreScriptsRefreshed(JNIEnv* env) const;
// Invokes scripts refreshment.
void RefreshScripts(JNIEnv* env);
// Called by the check manager when the saved passwords have been first loaded
// in memory. `count` is the number of saved passwords.
void OnSavedPasswordsFetched(int count) override;
......
......@@ -110,7 +110,7 @@ PasswordCheckManager::PasswordCheckManager(Profile* profile, Observer* observer)
PasswordCheckManager::~PasswordCheckManager() = default;
void PasswordCheckManager::StartCheck() {
if (!is_initialized_) {
if (!is_initialized_ || !AreScriptsRefreshed()) {
was_start_requested_ = true;
return;
}
......@@ -185,6 +185,9 @@ void PasswordCheckManager::OnSavedPasswordsChanged(
void PasswordCheckManager::OnCompromisedCredentialsChanged(
password_manager::CompromisedCredentialsManager::CredentialsView
credentials) {
if (!AreScriptsRefreshed()) {
credentials_count_to_notify_ = credentials.size();
}
observer_->OnCompromisedCredentialsChanged(credentials.size());
}
......@@ -216,10 +219,19 @@ void PasswordCheckManager::OnCredentialDone(
CompromisedCredentialForUI PasswordCheckManager::MakeUICredential(
const CredentialWithPassword& credential) const {
CompromisedCredentialForUI ui_credential(credential);
// UI is only be created after the list of available password check
// scripts has been refreshed.
DCHECK(AreScriptsRefreshed());
auto facet = password_manager::FacetURI::FromPotentiallyInvalidSpec(
credential.signon_realm);
ui_credential.display_username = GetDisplayUsername(credential.username);
ui_credential.has_script =
base::FeatureList::IsEnabled(
password_manager::features::kPasswordChangeInSettings) &&
password_script_fetcher_->IsScriptAvailable(
url::Origin::Create(credential.url.GetOrigin()));
if (facet.IsValidAndroidFacetURI()) {
const PasswordForm& android_form =
compromised_credentials_manager_.GetSavedPasswordsFor(credential)[0];
......@@ -286,3 +298,38 @@ bool PasswordCheckManager::CanUseAccountCheck() const {
return sync_state == SyncState::SYNCING_NORMAL_ENCRYPTION ||
sync_state == SyncState::ACCOUNT_PASSWORDS_ACTIVE_NORMAL_ENCRYPTION;
}
bool PasswordCheckManager::AreScriptsRefreshed() const {
// Don't fetch scripts if password change is not enabled.
return are_scripts_refreshed_ ||
!base::FeatureList::IsEnabled(
password_manager::features::kPasswordChangeInSettings);
}
void PasswordCheckManager::RefreshScripts() {
// Don't fetch scripts if password change is not enabled.
if (!base::FeatureList::IsEnabled(
password_manager::features::kPasswordChangeInSettings)) {
return;
}
are_scripts_refreshed_ = false;
password_script_fetcher_->RefreshScriptsIfNecessary(base::BindOnce(
&PasswordCheckManager::OnScriptsFetched, base::Unretained(this)));
}
void PasswordCheckManager::OnScriptsFetched() {
are_scripts_refreshed_ = true;
if (credentials_count_to_notify_.has_value()) {
// Inform the UI about compromised credentials another time because it was
// not allowed to generate UI before the availability of password scripts is
// known.
observer_->OnCompromisedCredentialsChanged(
credentials_count_to_notify_.value());
credentials_count_to_notify_.reset();
}
if (was_start_requested_)
StartCheck();
}
......@@ -6,13 +6,16 @@
#define CHROME_BROWSER_PASSWORD_CHECK_ANDROID_PASSWORD_CHECK_MANAGER_H_
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/strings/string_piece_forward.h"
#include "chrome/browser/password_check/android/password_check_ui_status.h"
#include "chrome/browser/password_manager/bulk_leak_check_service_factory.h"
#include "chrome/browser/password_manager/password_scripts_fetcher_factory.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/password_manager/core/browser/bulk_leak_check_service.h"
#include "components/password_manager/core/browser/bulk_leak_check_service_interface.h"
#include "components/password_manager/core/browser/password_scripts_fetcher.h"
#include "components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h"
#include "components/password_manager/core/browser/ui/compromised_credentials_manager.h"
#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
......@@ -82,6 +85,12 @@ class PasswordCheckManager
// UI update on completion.
void RemoveCredential(const password_manager::CredentialView& credential);
// Invokes `PasswordScriptsFetcher`'s scripts refreshment.
void RefreshScripts();
// Returns if scripts refreshment is finished.
bool AreScriptsRefreshed() const;
// Not copyable or movable
PasswordCheckManager(const PasswordCheckManager&) = delete;
PasswordCheckManager& operator=(const PasswordCheckManager&) = delete;
......@@ -122,6 +131,9 @@ class PasswordCheckManager
// in the account if the quota limit was reached.
bool CanUseAccountCheck() const;
// Callback when PasswordScriptsFetcher's cache has been warmed up.
void OnScriptsFetched();
// Obsever being notified of UI-relevant events.
// It must outlive `this`.
Observer* observer_ = nullptr;
......@@ -135,6 +147,12 @@ class PasswordCheckManager
PasswordStoreFactory::GetForProfile(profile_,
ServiceAccessType::EXPLICIT_ACCESS);
// Used to check whether autofill assistant scripts are available for
// the specified domain.
password_manager::PasswordScriptsFetcher* password_script_fetcher_ =
PasswordScriptsFetcherFactory::GetInstance()->GetForBrowserContext(
profile_);
// Used by `compromised_credentials_manager_` to obtain the list of saved
// passwords.
password_manager::SavedPasswordsPresenter saved_passwords_presenter_{
......@@ -161,6 +179,14 @@ class PasswordCheckManager
// Whether a check is currently running.
bool is_check_running_ = false;
// Whether scripts refreshement is finished.
bool are_scripts_refreshed_ = false;
// Latest number of changed compromised credentials while script fetching
// was running. If `credentials_count_to_notify_` has value, after scripts are
// fetched `onCompromisedCredentials` should be called.
base::Optional<size_t> credentials_count_to_notify_;
// A scoped observer for `saved_passwords_presenter_`.
ScopedObserver<password_manager::SavedPasswordsPresenter,
password_manager::SavedPasswordsPresenter::Observer>
......
......@@ -12,14 +12,17 @@
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "chrome/browser/password_check/android/password_check_ui_status.h"
#include "chrome/browser/password_manager/bulk_leak_check_service_factory.h"
#include "chrome/browser/password_manager/password_scripts_fetcher_factory.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "components/password_manager/core/browser/bulk_leak_check_service.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/test_password_store.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
......@@ -41,8 +44,10 @@ using password_manager::prefs::kLastTimePasswordCheckCompleted;
using testing::_;
using testing::ElementsAre;
using testing::Field;
using testing::Invoke;
using testing::IsEmpty;
using testing::NiceMock;
using testing::Return;
using CompromisedCredentialForUI =
PasswordCheckManager::CompromisedCredentialForUI;
......@@ -72,6 +77,21 @@ class MockPasswordCheckManagerObserver : public PasswordCheckManager::Observer {
(override));
};
class MockPasswordScriptsFetcher
: public password_manager::PasswordScriptsFetcher {
public:
MOCK_METHOD(void, PrewarmCache, (), (override));
MOCK_METHOD(void, RefreshScriptsIfNecessary, (base::OnceClosure), (override));
MOCK_METHOD(void,
FetchScriptAvailability,
(const url::Origin&, base::OnceCallback<void(bool)>),
(override));
MOCK_METHOD(bool, IsScriptAvailable, (const url::Origin&), (const override));
};
// TODO(crbug.com/1112804): Extract this into a password manager test utils
// file, since it's used across multiple tests.
scoped_refptr<TestPasswordStore> CreateAndUseTestPasswordStore(
......@@ -97,6 +117,15 @@ BulkLeakCheckService* CreateAndUseBulkLeakCheckService(
}));
}
MockPasswordScriptsFetcher* CreateAndUseMockPasswordScriptsFetcher(
Profile* profile) {
return PasswordScriptsFetcherFactory::GetInstance()
->SetTestingSubclassFactoryAndUse(
profile, base::BindRepeating([](content::BrowserContext*) {
return std::make_unique<MockPasswordScriptsFetcher>();
}));
}
PasswordForm MakeSavedPassword(base::StringPiece signon_realm,
base::StringPiece username,
base::StringPiece password = kPassword1,
......@@ -178,10 +207,10 @@ class PasswordCheckManagerTest : public testing::Test {
BulkLeakCheckService* service() { return service_; }
TestPasswordStore& store() { return *store_; }
protected:
NiceMock<MockPasswordCheckManagerObserver> mock_observer_;
std::unique_ptr<PasswordCheckManager> manager_;
MockPasswordCheckManagerObserver& mock_observer() { return mock_observer_; }
MockPasswordScriptsFetcher& fetcher() { return *fetcher_; }
PasswordCheckManager& manager() { return *manager_; }
base::test::ScopedFeatureList& feature_list() { return feature_list_; }
private:
content::BrowserTaskEnvironment task_env_;
......@@ -192,25 +221,29 @@ class PasswordCheckManagerTest : public testing::Test {
&profile_);
scoped_refptr<TestPasswordStore> store_ =
CreateAndUseTestPasswordStore(&profile_);
NiceMock<MockPasswordCheckManagerObserver> mock_observer_;
MockPasswordScriptsFetcher* fetcher_ =
CreateAndUseMockPasswordScriptsFetcher(&profile_);
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<PasswordCheckManager> manager_;
};
TEST_F(PasswordCheckManagerTest, SendsNoPasswordsMessageIfNoPasswordsAreSaved) {
EXPECT_CALL(mock_observer_, OnPasswordCheckStatusChanged(
PasswordCheckUIStatus::kErrorNoPasswords));
EXPECT_CALL(mock_observer(), OnPasswordCheckStatusChanged(
PasswordCheckUIStatus::kErrorNoPasswords));
InitializeManager();
RunUntilIdle();
}
TEST_F(PasswordCheckManagerTest, OnSavedPasswordsFetched) {
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1));
EXPECT_CALL(mock_observer_, OnSavedPasswordsFetched(1));
EXPECT_CALL(mock_observer(), OnSavedPasswordsFetched(1));
InitializeManager();
RunUntilIdle();
// Verify that OnSavedPasswordsFetched is not called after the initial fetch
// even if the saved passwords change.
EXPECT_CALL(mock_observer_, OnSavedPasswordsFetched(_)).Times(0);
EXPECT_CALL(mock_observer(), OnSavedPasswordsFetched(_)).Times(0);
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername2));
RunUntilIdle();
}
......@@ -219,12 +252,12 @@ TEST_F(PasswordCheckManagerTest, OnCompromisedCredentialsChanged) {
// This is called on multiple events: once for saved passwords retrieval,
// once for compromised credentials retrieval and once when the saved password
// is added.
EXPECT_CALL(mock_observer_, OnCompromisedCredentialsChanged(0)).Times(3);
EXPECT_CALL(mock_observer(), OnCompromisedCredentialsChanged(0)).Times(3);
InitializeManager();
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1));
RunUntilIdle();
EXPECT_CALL(mock_observer_, OnCompromisedCredentialsChanged(1));
EXPECT_CALL(mock_observer(), OnCompromisedCredentialsChanged(1));
store().AddCompromisedCredentials(MakeCompromised(kExampleCom, kUsername1));
RunUntilIdle();
}
......@@ -235,7 +268,7 @@ TEST_F(PasswordCheckManagerTest, CorrectlyCreatesUIStructForSiteCredential) {
store().AddCompromisedCredentials(MakeCompromised(kExampleCom, kUsername1));
RunUntilIdle();
EXPECT_THAT(
manager_->GetCompromisedCredentials(),
manager().GetCompromisedCredentials(),
ElementsAre(ExpectCompromisedCredentialForUI(
base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.com"),
base::nullopt, "https://example.com/",
......@@ -258,7 +291,7 @@ TEST_F(PasswordCheckManagerTest, CorrectlyCreatesUIStructForAppCredentials) {
RunUntilIdle();
EXPECT_THAT(
manager_->GetCompromisedCredentials(),
manager().GetCompromisedCredentials(),
UnorderedElementsAre(
ExpectCompromisedCredentialForUI(
base::ASCIIToUTF16(kUsername1),
......@@ -278,11 +311,11 @@ TEST_F(PasswordCheckManagerTest, SetsTimestampOnSuccessfulCheck) {
RunUntilIdle();
// Pretend to start the check so that the manager thinks a check is running.
manager_->StartCheck();
manager().StartCheck();
// Change the state to idle to simulate a successful check finish.
service()->set_state_and_notify(State::kIdle);
EXPECT_NE(0.0, manager_->GetLastCheckTimestamp().ToDoubleT());
EXPECT_NE(0.0, manager().GetLastCheckTimestamp().ToDoubleT());
}
TEST_F(PasswordCheckManagerTest, DoesntRecordTimestampOfUnsuccessfulCheck) {
......@@ -291,11 +324,34 @@ TEST_F(PasswordCheckManagerTest, DoesntRecordTimestampOfUnsuccessfulCheck) {
RunUntilIdle();
// Pretend to start the check so that the manager thinks a check is running.
manager_->StartCheck();
manager().StartCheck();
// Change the state to an error state to simulate a unsuccessful check finish.
service()->set_state_and_notify(State::kSignedOut);
EXPECT_EQ(0.0, manager_->GetLastCheckTimestamp().ToDoubleT());
EXPECT_EQ(0.0, manager().GetLastCheckTimestamp().ToDoubleT());
}
TEST_F(PasswordCheckManagerTest, CorrectlyCreatesUIStructWithPasswordScripts) {
InitializeManager();
feature_list().InitAndEnableFeature(
password_manager::features::kPasswordChangeInSettings);
store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1));
store().AddCompromisedCredentials(MakeCompromised(kExampleCom, kUsername1));
RunUntilIdle();
EXPECT_CALL(fetcher(), RefreshScriptsIfNecessary)
.WillOnce(Invoke(
[](base::OnceClosure callback) { std::move(callback).Run(); }));
manager().RefreshScripts();
EXPECT_CALL(fetcher(), IsScriptAvailable).WillRepeatedly(Return(true));
EXPECT_THAT(
manager().GetCompromisedCredentials(),
ElementsAre(ExpectCompromisedCredentialForUI(
base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.com"),
base::nullopt, "https://example.com/",
CompromiseTypeFlags::kCredentialLeaked, /*has_script=*/true)));
}
TEST_F(PasswordCheckManagerTest, GetCompromisedCredentialsOrder) {
......@@ -321,7 +377,7 @@ TEST_F(PasswordCheckManagerTest, GetCompromisedCredentialsOrder) {
CompromiseType::kPhished));
RunUntilIdle();
EXPECT_THAT(
manager_->GetCompromisedCredentials(),
manager().GetCompromisedCredentials(),
ElementsAre(
ExpectCompromisedCredentialForUI(
base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.org"),
......
......@@ -7,6 +7,7 @@ import("//build/config/android/rules.gni")
android_library("java") {
deps = [
"//base:base_java",
"//base:jni_java",
"//chrome/browser/settings:java",
"//components/password_manager/core/browser:password_manager_java_enums",
"//third_party/android_deps:androidx_annotation_annotation_java",
......@@ -14,9 +15,16 @@ android_library("java") {
]
sources = [
"java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordScriptsFetcherBridge.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/PasswordReauthenticationFragment.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/ReauthenticationManager.java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
generate_jni("jni_headers") {
visibility = [ "//chrome/browser" ]
sources = [ "java/src/org/chromium/chrome/browser/password_manager/PasswordScriptsFetcherBridge.java" ]
}
junit_binary("password_manager_junit_tests") {
......
......@@ -6,20 +6,18 @@ package org.chromium.chrome.browser.password_manager;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.components.embedder_support.browser_context.BrowserContextHandle;
/**
* Android bridge to |PasswordScriptsFetcher|.
*/
@JNINamespace("password_manager")
public class PasswordScriptsFetcherBridge {
public static void prewarmCache(Profile profile) {
PasswordScriptsFetcherBridgeJni.get().prewarmCache(profile);
public static void prewarmCache() {
PasswordScriptsFetcherBridgeJni.get().prewarmCache();
}
@NativeMethods
interface Natives {
void prewarmCache(BrowserContextHandle browserContext);
void prewarmCache();
}
}
......@@ -5,24 +5,17 @@
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "chrome/android/chrome_jni_headers/PasswordScriptsFetcherBridge_jni.h"
#include "chrome/browser/password_manager/android/jni_headers/PasswordScriptsFetcherBridge_jni.h"
#include "chrome/browser/password_manager/password_scripts_fetcher_factory.h"
#include "components/embedder_support/android/browser_context/browser_context_handle.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/password_manager/core/browser/password_scripts_fetcher.h"
#include "content/public/browser/browser_context.h"
namespace password_manager {
// static
void JNI_PasswordScriptsFetcherBridge_PrewarmCache(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jbrowser_context) {
content::BrowserContext* context =
browser_context::BrowserContextFromJavaHandle(jbrowser_context);
DCHECK(context);
void JNI_PasswordScriptsFetcherBridge_PrewarmCache(JNIEnv* env) {
PasswordScriptsFetcherFactory::GetInstance()
->GetForBrowserContext(context)
->GetForBrowserContext(ProfileManager::GetLastUsedProfile())
->PrewarmCache();
}
......
......@@ -9,6 +9,8 @@ import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.password_manager.PasswordScriptsFetcherBridge;
import org.chromium.chrome.browser.settings.SettingsLauncher;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
......@@ -32,6 +34,10 @@ public class SafetyCheckCoordinator implements DefaultLifecycleObserver {
public static void create(SafetyCheckSettingsFragment settingsFragment,
SafetyCheckUpdatesDelegate updatesClient, SettingsLauncher settingsLauncher) {
new SafetyCheckCoordinator(settingsFragment, updatesClient, settingsLauncher);
if (ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORD_CHANGE_IN_SETTINGS)) {
// Triggers pre-fetching the list of password change scripts.
PasswordScriptsFetcherBridge.prewarmCache();
}
}
private SafetyCheckCoordinator(SafetyCheckSettingsFragment settingsFragment,
......
......@@ -742,7 +742,6 @@ static_library("ui") {
"android/passwords/password_generation_dialog_view_android.h",
"android/passwords/password_generation_editing_popup_view_android.cc",
"android/passwords/password_generation_editing_popup_view_android.h",
"android/passwords/password_scripts_fetcher_android.cc",
"android/safe_browsing/password_reuse_dialog_view_android.cc",
"android/safe_browsing/password_reuse_dialog_view_android.h",
"android/simple_message_box_android.cc",
......
......@@ -27,7 +27,8 @@ class PasswordScriptsFetcher : public KeyedService {
// |PrewarmCache| was supposed to fetch the data in advance). In case of
// several calls of the method, the callbacks will be called one after
// another.
virtual void Fetch(base::OnceClosure fetch_finished_callback) = 0;
virtual void RefreshScriptsIfNecessary(
base::OnceClosure fetch_finished_callback) = 0;
// Returns whether there is a password change script for |origin| via
// |callback|. If the cache was never set or is stale, it triggers a re-fetch.
// In case of a network error, the verdict will default to no script being
......
......@@ -86,7 +86,7 @@ void PasswordScriptsFetcherImpl::PrewarmCache() {
StartFetch();
}
void PasswordScriptsFetcherImpl::Fetch(
void PasswordScriptsFetcherImpl::RefreshScriptsIfNecessary(
base::OnceClosure fetch_finished_callback) {
CacheState state = IsCacheStale()
? (url_loader_ ? CacheState::kWaiting
......
......@@ -64,7 +64,8 @@ class PasswordScriptsFetcherImpl
// PasswordScriptsFetcher:
void PrewarmCache() override;
void Fetch(base::OnceClosure fetch_finished_callback) override;
void RefreshScriptsIfNecessary(
base::OnceClosure fetch_finished_callback) override;
void FetchScriptAvailability(const url::Origin& origin,
ResponseCallback callback) override;
bool IsScriptAvailable(const url::Origin& origin) const override;
......
......@@ -88,7 +88,7 @@ class PasswordScriptsFetcherImplTest : public ::testing::Test {
void StartBulkCheck() {
pending_fetch_finished_callbacks_++;
fetcher()->Fetch(
fetcher()->RefreshScriptsIfNecessary(
base::BindOnce(&PasswordScriptsFetcherImplTest::RecordFetchFinished,
base::Unretained(this)));
RequestSingleScriptAvailability(GetOriginWithScript1());
......
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