blob: 0e2da162f54008a7c63b1af56c430505f42ee075 [file] [log] [blame]
// 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.
package org.chromium.chrome.browser.preferences.password;
import org.chromium.base.ObserverList;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
/**
* A provider for PasswordManagerHandler implementations, handling the choice of the proper one
* (production vs. testing), its lifetime and multiple observers.
*
* This class is used by the code responsible for Chrome's passwords settings. There can only be one
* instance of Chrome's passwords settings opened at a time (although more clients of
* PasswordManagerHandler can live as nested settings pages). Therefore, the provider can be just
* one. However, it cannot be just a collection of static data members and methods, because the
* managed PasswordManagerHandler instances need to refer to it as an observer. For that reason, the
* provider is a singleton.
*/
public class PasswordManagerHandlerProvider implements PasswordManagerHandler.PasswordListObserver {
private static final class LazyHolder {
private static final PasswordManagerHandlerProvider INSTANCE =
new PasswordManagerHandlerProvider();
}
/** Private constructor, use GetInstance() instead. */
private PasswordManagerHandlerProvider() {}
public static PasswordManagerHandlerProvider getInstance() {
return LazyHolder.INSTANCE;
}
// The production implementation of PasswordManagerHandler is |sPasswordUIView|, instantiated on
// demand. Tests might want to override that by providing a fake implementation through
// setPasswordManagerHandlerForTest, which is then kept in |mTestPasswordManagerHandler|.
private PasswordUIView mPasswordUIView;
private PasswordManagerHandler mTestPasswordManagerHandler;
// This class is itself a PasswordListObserver, listening directly to a PasswordManagerHandler
// implementation. But it also keeps a list of other observers, to which it forwards the events.
private final ObserverList<PasswordManagerHandler.PasswordListObserver> mObservers =
new ObserverList<PasswordManagerHandler.PasswordListObserver>();
/**
* Sets a testing implementation of PasswordManagerHandler to be used. It overrides the
* production one even if it exists. The caller needs to ensure that |this| becomes an observer
* of |passwordManagerHandler|. Also, this must not be called when there are already some
* observers in |mObservers|, because of special handling of the production implementation of
* PasswordManagerHandler on removing the last observer.
*/
@VisibleForTesting
public void setPasswordManagerHandlerForTest(PasswordManagerHandler passwordManagerHandler) {
ThreadUtils.assertOnUiThread();
assert mObservers.isEmpty();
mTestPasswordManagerHandler = passwordManagerHandler;
}
/**
* A convenience function to choose between the production and test PasswordManagerHandler
* implementation.
*/
public PasswordManagerHandler getPasswordManagerHandler() {
ThreadUtils.assertOnUiThread();
if (mTestPasswordManagerHandler != null) return mTestPasswordManagerHandler;
return mPasswordUIView;
}
/**
* This method creates the production implementation of PasswordManagerHandler and saves it into
* mPasswordUIView.
*/
private void createPasswordManagerHandler() {
ThreadUtils.assertOnUiThread();
assert mPasswordUIView == null;
mPasswordUIView = new PasswordUIView(this);
}
/**
* Starts forwarding events from the PasswordManagerHandler implementation to |observer|.
*/
public void addObserver(PasswordManagerHandler.PasswordListObserver observer) {
ThreadUtils.assertOnUiThread();
if (getPasswordManagerHandler() == null) createPasswordManagerHandler();
mObservers.addObserver(observer);
}
public void removeObserver(PasswordManagerHandler.PasswordListObserver observer) {
ThreadUtils.assertOnUiThread();
mObservers.removeObserver(observer);
// If this was the last observer of the production implementation of PasswordManagerHandler,
// call destroy on it to close the connection to the native C++ code.
if (mObservers.isEmpty() && mTestPasswordManagerHandler == null) {
mPasswordUIView.destroy();
mPasswordUIView = null;
}
}
@Override
public void passwordListAvailable(int count) {
ThreadUtils.assertOnUiThread();
for (PasswordManagerHandler.PasswordListObserver observer : mObservers) {
observer.passwordListAvailable(count);
}
}
@Override
public void passwordExceptionListAvailable(int count) {
ThreadUtils.assertOnUiThread();
for (PasswordManagerHandler.PasswordListObserver observer : mObservers) {
observer.passwordExceptionListAvailable(count);
}
}
}