chromium/ios/web/web_state/policy_decision_state_tracker.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef IOS_WEB_WEB_STATE_POLICY_DECISION_STATE_TRACKER_H_
#define IOS_WEB_WEB_STATE_POLICY_DECISION_STATE_TRACKER_H_

#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#import "ios/web/public/navigation/web_state_policy_decider.h"

namespace web {

// Tracks responses received from WebStatePolicyDeciders for calls to
// shouldAllowResponse(), maintaining the current result based on decisions
// received so far, and calling a callback once all decisions have been
// received. If all decisions are PolicyDecision::Allow, the final result is
// also Allow. If at least one decision is PolicyDecision::Cancel, the final
// result is Cancel. Otherwise, if at least one decision is
// CancelAndDisplayError, the final result is also CancelAndDisplayError, with
// the error associated with the first such decision. If this is destroyed
// before all decisions have been received and the callback has not yet been
// invoked, the callback is invoked with PolicyDecision::Cancel.
class PolicyDecisionStateTracker final {
 public:
  // Constructor that takes a `callback` to be called once all decisions have
  // been received.
  PolicyDecisionStateTracker(
      WebStatePolicyDecider::PolicyDecisionCallback callback);

  ~PolicyDecisionStateTracker();

  // Called by each WebStatePolicyDecider with its `decision`.
  void OnSinglePolicyDecisionReceived(
      WebStatePolicyDecider::PolicyDecision decision);

  // Returns true if the final result has already been determined. This means
  // that either every decider has provided a decision, or the decisions
  // received so far mean that the final result won't change no matter what
  // other decisions are received.
  bool DeterminedFinalResult();

  // Called once all WebStatePolicyDeciders have been asked for a decision,
  // where `num_decisions_requested` is the number of WebStatePolicyDeciders
  // that have been asked for a decision.
  void FinishedRequestingDecisions(int num_decisions_requested);

 private:
  // Called once, after all WebStatePolicyDeciders have provided a decision or
  // after the decisions received so far have already determined the final
  // result.
  void OnFinalResultDetermined();

  // Called once with the final result.
  base::OnceCallback<void(WebStatePolicyDecider::PolicyDecision)> callback_;

  // The current result, based on the decisions received so far.
  WebStatePolicyDecider::PolicyDecision result_ =
      WebStatePolicyDecider::PolicyDecision::Allow();

  // The number of WebStatePolicyDeciders that have provided a decision.
  int num_decisions_received_ = 0;

  // Called after each decision is received. Initially, this is a no-op. Once
  // all decisions have been requested, this is changed to a BarrierClosure
  // that handles invoking `callback_` after receiving the remaining
  // outstanding decisions.
  base::RepeatingClosure decision_closure_ = base::DoNothing();

  base::WeakPtrFactory<PolicyDecisionStateTracker> weak_ptr_factory_{this};
};

}  // namespace web

#endif  // IOS_WEB_WEB_STATE_POLICY_DECISION_STATE_TRACKER_H_