Commit 34026dc2 authored by Mathias Carlen's avatar Mathias Carlen Committed by Commit Bot

[Autofill Assistant] Use base::ObserverList for navigation listeners.

Before this patch, the observer/listener for navigation state changes
were implemented using a std::vector. This would lead to null pointer
dereferencing when adding and removing listeners while handling an event
due to std::vector::erase reordering elements (pointers). This patch
uses a checked observer and ObserverList instead to properly handling
adding and removing observers/listeners.

This bug was uncovered while working on the bug linked in this patch.

R=arbesser@google.com

Bug: b/152724265
Change-Id: Ia59bfca5bec5ce326394668eb7d8d62004fc16fb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2139691
Commit-Queue: Mathias Carlen <mcarlen@chromium.org>
Reviewed-by: default avatarClemens Arbesser <arbesser@google.com>
Cr-Commit-Position: refs/heads/master@{#757021}
parent ac4ae1e6
...@@ -315,16 +315,12 @@ void Controller::ClearGenericUi() { ...@@ -315,16 +315,12 @@ void Controller::ClearGenericUi() {
} }
} }
void Controller::AddListener(ScriptExecutorDelegate::Listener* listener) { void Controller::AddListener(NavigationListener* listener) {
auto found = std::find(listeners_.begin(), listeners_.end(), listener); navigation_listeners_.AddObserver(listener);
if (found == listeners_.end())
listeners_.emplace_back(listener);
} }
void Controller::RemoveListener(ScriptExecutorDelegate::Listener* listener) { void Controller::RemoveListener(NavigationListener* listener) {
auto found = std::find(listeners_.begin(), listeners_.end(), listener); navigation_listeners_.RemoveObserver(listener);
if (found != listeners_.end())
listeners_.erase(found);
} }
void Controller::SetExpandSheetForPromptAction(bool expand) { void Controller::SetExpandSheetForPromptAction(bool expand) {
...@@ -575,9 +571,8 @@ const ClientSettings& Controller::GetClientSettings() const { ...@@ -575,9 +571,8 @@ const ClientSettings& Controller::GetClientSettings() const {
} }
void Controller::ReportNavigationStateChanged() { void Controller::ReportNavigationStateChanged() {
// Listeners are called in the same order they were added. for (auto& listener : navigation_listeners_) {
for (auto* listener : listeners_) { listener.OnNavigationStateChanged();
listener->OnNavigationStateChanged();
} }
} }
......
...@@ -142,8 +142,8 @@ class Controller : public ScriptExecutorDelegate, ...@@ -142,8 +142,8 @@ class Controller : public ScriptExecutorDelegate,
// states where showing the UI is optional, such as RUNNING, in tracking mode. // states where showing the UI is optional, such as RUNNING, in tracking mode.
void RequireUI() override; void RequireUI() override;
void AddListener(ScriptExecutorDelegate::Listener* listener) override; void AddListener(NavigationListener* listener) override;
void RemoveListener(ScriptExecutorDelegate::Listener* listener) override; void RemoveListener(NavigationListener* listener) override;
void SetExpandSheetForPromptAction(bool expand) override; void SetExpandSheetForPromptAction(bool expand) override;
...@@ -406,7 +406,7 @@ class Controller : public ScriptExecutorDelegate, ...@@ -406,7 +406,7 @@ class Controller : public ScriptExecutorDelegate,
// Value for ScriptExecutorDelegate::HasNavigationError() // Value for ScriptExecutorDelegate::HasNavigationError()
bool navigation_error_ = false; bool navigation_error_ = false;
std::vector<ScriptExecutorDelegate::Listener*> listeners_; base::ObserverList<NavigationListener> navigation_listeners_;
// Tracks scripts and script execution. It's kept at the end, as it tend to // Tracks scripts and script execution. It's kept at the end, as it tend to
// depend on everything the controller support, through script and script // depend on everything the controller support, through script and script
......
...@@ -246,11 +246,12 @@ std::ostream& operator<<(std::ostream& out, const NavigationState& state) { ...@@ -246,11 +246,12 @@ std::ostream& operator<<(std::ostream& out, const NavigationState& state) {
// A Listener that keeps track of the reported state of the delegate captured // A Listener that keeps track of the reported state of the delegate captured
// from OnNavigationStateChanged. // from OnNavigationStateChanged.
class NavigationStateChangeListener : public ScriptExecutorDelegate::Listener { class NavigationStateChangeListener
: public ScriptExecutorDelegate::NavigationListener {
public: public:
explicit NavigationStateChangeListener(ScriptExecutorDelegate* delegate) explicit NavigationStateChangeListener(ScriptExecutorDelegate* delegate)
: delegate_(delegate) {} : delegate_(delegate) {}
~NavigationStateChangeListener() = default; ~NavigationStateChangeListener() override;
void OnNavigationStateChanged() override; void OnNavigationStateChanged() override;
std::vector<NavigationState> events; std::vector<NavigationState> events;
...@@ -259,6 +260,8 @@ class NavigationStateChangeListener : public ScriptExecutorDelegate::Listener { ...@@ -259,6 +260,8 @@ class NavigationStateChangeListener : public ScriptExecutorDelegate::Listener {
ScriptExecutorDelegate* const delegate_; ScriptExecutorDelegate* const delegate_;
}; };
NavigationStateChangeListener::~NavigationStateChangeListener() {}
void NavigationStateChangeListener::OnNavigationStateChanged() { void NavigationStateChangeListener::OnNavigationStateChanged() {
NavigationState state; NavigationState state;
state.navigating = delegate_->IsNavigatingToNewDocument(); state.navigating = delegate_->IsNavigatingToNewDocument();
......
...@@ -161,11 +161,11 @@ void FakeScriptExecutorDelegate::RequireUI() { ...@@ -161,11 +161,11 @@ void FakeScriptExecutorDelegate::RequireUI() {
require_ui_ = true; require_ui_ = true;
} }
void FakeScriptExecutorDelegate::AddListener(Listener* listener) { void FakeScriptExecutorDelegate::AddListener(NavigationListener* listener) {
listeners_.insert(listener); listeners_.insert(listener);
} }
void FakeScriptExecutorDelegate::RemoveListener(Listener* listener) { void FakeScriptExecutorDelegate::RemoveListener(NavigationListener* listener) {
listeners_.erase(listener); listeners_.erase(listener);
} }
......
...@@ -66,8 +66,8 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate { ...@@ -66,8 +66,8 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate {
bool HasNavigationError() override; bool HasNavigationError() override;
bool IsNavigatingToNewDocument() override; bool IsNavigatingToNewDocument() override;
void RequireUI() override; void RequireUI() override;
void AddListener(Listener* listener) override; void AddListener(NavigationListener* listener) override;
void RemoveListener(Listener* listener) override; void RemoveListener(NavigationListener* listener) override;
void SetExpandSheetForPromptAction(bool expand) override; void SetExpandSheetForPromptAction(bool expand) override;
void SetGenericUi( void SetGenericUi(
std::unique_ptr<GenericUserInterfaceProto> generic_ui, std::unique_ptr<GenericUserInterfaceProto> generic_ui,
...@@ -136,7 +136,7 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate { ...@@ -136,7 +136,7 @@ class FakeScriptExecutorDelegate : public ScriptExecutorDelegate {
std::unique_ptr<UserData> payment_request_info_; std::unique_ptr<UserData> payment_request_info_;
bool navigating_to_new_document_ = false; bool navigating_to_new_document_ = false;
bool navigation_error_ = false; bool navigation_error_ = false;
std::set<ScriptExecutorDelegate::Listener*> listeners_; std::set<ScriptExecutorDelegate::NavigationListener*> listeners_;
ViewportMode viewport_mode_ = ViewportMode::NO_RESIZE; ViewportMode viewport_mode_ = ViewportMode::NO_RESIZE;
ConfigureBottomSheetProto::PeekMode peek_mode_ = ConfigureBottomSheetProto::PeekMode peek_mode_ =
ConfigureBottomSheetProto::HANDLE; ConfigureBottomSheetProto::HANDLE;
......
...@@ -32,7 +32,7 @@ class UserModel; ...@@ -32,7 +32,7 @@ class UserModel;
// Class to execute an assistant script. // Class to execute an assistant script.
class ScriptExecutor : public ActionDelegate, class ScriptExecutor : public ActionDelegate,
public ScriptExecutorDelegate::Listener { public ScriptExecutorDelegate::NavigationListener {
public: public:
// Listens to events on ScriptExecutor. // Listens to events on ScriptExecutor.
// TODO(b/806868): Make global_payload a part of callback instead of the // TODO(b/806868): Make global_payload a part of callback instead of the
...@@ -237,7 +237,7 @@ class ScriptExecutor : public ActionDelegate, ...@@ -237,7 +237,7 @@ class ScriptExecutor : public ActionDelegate,
// Helper for WaitForElementVisible that keeps track of the state required to // Helper for WaitForElementVisible that keeps track of the state required to
// run interrupts while waiting for a specific element. // run interrupts while waiting for a specific element.
class WaitForDomOperation : public ScriptExecutor::Listener, class WaitForDomOperation : public ScriptExecutor::Listener,
ScriptExecutorDelegate::Listener { ScriptExecutorDelegate::NavigationListener {
public: public:
// Let the caller know about either the result of looking for the element or // Let the caller know about either the result of looking for the element or
// of an abnormal result from an interrupt. // of an abnormal result from an interrupt.
...@@ -267,7 +267,7 @@ class ScriptExecutor : public ActionDelegate, ...@@ -267,7 +267,7 @@ class ScriptExecutor : public ActionDelegate,
void Pause(); void Pause();
void Continue(); void Continue();
// Implements ScriptExecutorDelegate::Listener // Implements ScriptExecutorDelegate::NavigationListener
void OnNavigationStateChanged() override; void OnNavigationStateChanged() override;
// Implements ScriptExecutor::Listener // Implements ScriptExecutor::Listener
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/observer_list_types.h"
#include "components/autofill_assistant/browser/client_status.h" #include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/details.h" #include "components/autofill_assistant/browser/details.h"
#include "components/autofill_assistant/browser/info_box.h" #include "components/autofill_assistant/browser/info_box.h"
...@@ -39,7 +40,7 @@ class UserModel; ...@@ -39,7 +40,7 @@ class UserModel;
class ScriptExecutorDelegate { class ScriptExecutorDelegate {
public: public:
class Listener { class NavigationListener : public base::CheckedObserver {
public: public:
// The values returned by IsNavigatingToNewDocument() or // The values returned by IsNavigatingToNewDocument() or
// HasNavigationError() might have changed. // HasNavigationError() might have changed.
...@@ -128,11 +129,11 @@ class ScriptExecutorDelegate { ...@@ -128,11 +129,11 @@ class ScriptExecutorDelegate {
// Register a listener that can be told about changes. Duplicate calls are // Register a listener that can be told about changes. Duplicate calls are
// ignored. // ignored.
virtual void AddListener(Listener* listener) = 0; virtual void AddListener(NavigationListener* listener) = 0;
// Removes a previously registered listener. Does nothing if no such listeners // Removes a previously registered listener. Does nothing if no such listeners
// exists. // exists.
virtual void RemoveListener(Listener* listener) = 0; virtual void RemoveListener(NavigationListener* listener) = 0;
// Set how the sheet should behave when entering a prompt state. // Set how the sheet should behave when entering a prompt state.
virtual void SetExpandSheetForPromptAction(bool expand) = 0; virtual void SetExpandSheetForPromptAction(bool expand) = 0;
......
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