// 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 CONTENT_BROWSER_MOJO_BINDER_POLICY_APPLIER_H_
#define CONTENT_BROWSER_MOJO_BINDER_POLICY_APPLIER_H_

#include <string>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ref.h"
#include "content/browser/mojo_binder_policy_map_impl.h"
#include "content/common/content_export.h"

namespace content {

// MojoBinderPolicyApplier is a helper class for `BrowserInterfaceBrokerImpl`
// which allows control over when to run the binder registered for a
// requested interface. This is useful in cases like prerendering pages, where
// it can be desirable to defer binding until the page is activated, or take
// other actions.
//
// The action to take for each interface is specified in the given
// `MojoBinderPolicyMap`, and kDefer is used when no policy is specified.
//
// See content/browser/preloading/prerender/README.md for more about capability
// control.
class CONTENT_EXPORT MojoBinderPolicyApplier {
 public:
  enum class Mode {
    // In the kEnforce mode, MojoBinderPolicyApplier processes binding requests
    // strictly according to the pre-set policies.
    kEnforce,
    // If the page is about to activate, MojoBinderPolicyApplier will switch to
    // the kPrepareToGrantAll mode, and all non-kGrant binders will be
    // deferred.
    kPrepareToGrantAll,
    // In the kGrantAll mode, MojoBinderPolicyApplier grants all binding
    // requests regardless of their policies.
    kGrantAll,
  };

  // `policy_map` must outlive `this` and must not be null.
  // `cancel_callback` will be executed when ApplyPolicyToBinder() processes a
  // kCancel interface.
  MojoBinderPolicyApplier(
      const MojoBinderPolicyMapImpl* policy_map,
      base::OnceCallback<void(const std::string& interface_name)>
          cancel_callback);
  ~MojoBinderPolicyApplier();

  // Returns the instance used by BrowserInterfaceBrokerImpl for same-origin
  // prerendering pages. This is used when the prerendered page and the page
  // that triggered the prerendering are same origin.
  static std::unique_ptr<MojoBinderPolicyApplier>
  CreateForSameOriginPrerendering(
      base::OnceCallback<void(const std::string& interface_name)>
          cancel_closure);

  // Returns the instance used by BrowserInterfaceBrokerImpl for preview mode.
  // This is used when a page is shown in preview mode.
  static std::unique_ptr<MojoBinderPolicyApplier> CreateForPreview(
      base::OnceCallback<void(const std::string& interface_name)>
          cancel_closure);

  // Disallows copy and move operations.
  MojoBinderPolicyApplier(const MojoBinderPolicyApplier& other) = delete;
  MojoBinderPolicyApplier& operator=(const MojoBinderPolicyApplier& other) =
      delete;
  MojoBinderPolicyApplier(MojoBinderPolicyApplier&&) = delete;
  MojoBinderPolicyApplier& operator=(MojoBinderPolicyApplier&&) = delete;

  // Applies `MojoBinderNonAssociatedPolicy` before binding a non-associated
  // interface.
  // - In kEnforce mode:
  //   - kGrant: Runs `binder_callback` immediately.
  //   - kDefer: Saves `binder_callback` and runs it when GrantAll() is called.
  //   - kCancel: Drops `binder_callback` and runs `cancel_callback_`.
  //   - kUnexpected: Unimplemented now.
  // - In the kPrepareToGrantAll mode:
  //   - kGrant: Runs `binder_callback` immediately.
  //   - kDefer, kCancel and kUnexpected: Saves `binder_callback` and runs it
  //   when GrantAll() is called.
  // - In the kGrantAll mode: this always runs the callback immediately.
  void ApplyPolicyToNonAssociatedBinder(const std::string& interface_name,
                                        base::OnceClosure binder_callback);

  // Applies `MojoBinderAssociatedPolicy` before binding an associated
  // interface. Note that this method only applies kCancel and kGrant to
  // associated intefaces, because messages sent over associated interfaces
  // cannot be deferred. See
  // https://chromium.googlesource.com/chromium/src/+/HEAD/mojo/public/cpp/bindings/README.md#Associated-Interfaces
  // for more information.
  // Runs the cancellation callback and returns false if kCancel is applied.
  // Otherwise returns true.
  bool ApplyPolicyToAssociatedBinder(const std::string& interface_name);

  // Switches this to the kPrepareToGrantAll mode.
  void PrepareToGrantAll();

  // Runs all deferred binders and runs binder callbacks for all subsequent
  // requests, i.e., it stops applying the policies.

  void GrantAll();
  // Deletes all deferred binders without running them.
  void DropDeferredBinders();

 private:
  friend class MojoBinderPolicyApplierTest;

  // Gets the corresponding policy of the given mojo interface name.
  MojoBinderNonAssociatedPolicy GetNonAssociatedMojoBinderPolicy(
      const std::string& interface_name) const;

  const MojoBinderNonAssociatedPolicy default_policy_ =
      MojoBinderNonAssociatedPolicy::kDefer;
  // Maps Mojo interface name to its policy.
  const raw_ref<const MojoBinderPolicyMapImpl> policy_map_;

  // Will be executed upon a request for a kCancel interface.
  base::OnceCallback<void(const std::string& interface_name)> cancel_callback_;
  Mode mode_ = Mode::kEnforce;

  // Stores binders which are delayed running.
  std::vector<base::OnceClosure> deferred_binders_;

  // Stores binders that can be used to send synchronous messages but
  // are delayed running.
  std::vector<base::OnceClosure> deferred_sync_binders_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_MOJO_BINDER_POLICY_APPLIER_H_