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

#ifndef COMPONENTS_PERMISSIONS_OBJECT_PERMISSION_CONTEXT_BASE_H_
#define COMPONENTS_PERMISSIONS_OBJECT_PERMISSION_CONTEXT_BASE_H_

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/values.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/keyed_service/core/keyed_service.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"

class HostContentSettingsMap;

namespace url {
class Origin;
}

namespace permissions {

// This is the base class for services that manage any type of permission that
// is associated with a more complicated grant than simple allow/deny. This is
// typically granted through a chooser-style UI.
// Subclasses must define the structure of the objects that are stored.
class ObjectPermissionContextBase : public KeyedService {
 public:
  struct Object {
    Object(const url::Origin& origin,
           base::Value value,
           content_settings::SettingSource source,
           bool incognito);
    ~Object();
    std::unique_ptr<Object> Clone();

    GURL origin;
    base::Value value;
    content_settings::SettingSource source;
    bool incognito;
  };

  using ObjectMap =
      std::map<url::Origin, std::map<std::string, std::unique_ptr<Object>>>;

  // This observer can be used to be notified of changes to the permission of
  // an object.
  class PermissionObserver : public base::CheckedObserver {
   public:
    // Notify observer that an object permission changed for the permission
    // context represented by |guard_content_settings_type|, if applicable, and
    // |data_content_settings_type|.
    virtual void OnObjectPermissionChanged(
        absl::optional<ContentSettingsType> guard_content_settings_type,
        ContentSettingsType data_content_settings_type);
    // Notify observer that an object permission was revoked for |origin|.
    virtual void OnPermissionRevoked(const url::Origin& origin);
  };

  void AddObserver(PermissionObserver* observer);
  void RemoveObserver(PermissionObserver* observer);

  ObjectPermissionContextBase(
      ContentSettingsType guard_content_settings_type,
      ContentSettingsType data_content_settings_type,
      HostContentSettingsMap* host_content_settings_map);
  ObjectPermissionContextBase(
      ContentSettingsType data_content_settings_type,
      HostContentSettingsMap* host_content_settings_map);
  ~ObjectPermissionContextBase() override;

  // Checks whether |origin| can request permission to access objects. This is
  // done by checking |guard_content_settings_type_| which will usually be "ask"
  // by default but could be set by the user or group policy.
  bool CanRequestObjectPermission(const url::Origin& origin);

  // Returns the object corresponding to |key| that |origin| has been granted
  // permission to access. This method should only be called if
  // |GetKeyForObject()| is overridden to return sensible keys.
  //
  // This method may be extended by a subclass to return
  // objects not stored in |host_content_settings_map_|.
  virtual std::unique_ptr<Object> GetGrantedObject(const url::Origin& origin,
                                                   const base::StringPiece key);

  // Returns the list of objects that |origin| has been granted permission to
  // access. This method may be extended by a subclass to return objects not
  // stored in |host_content_settings_map_|.
  virtual std::vector<std::unique_ptr<Object>> GetGrantedObjects(
      const url::Origin& origin);

  // Returns the list of all origins that have granted permission(s).
  virtual std::vector<url::Origin> GetOriginsWithGrants();

  // Returns the set of all objects that any origin has been granted permission
  // to access.
  //
  // This method may be extended by a subclass to return objects not stored in
  // |host_content_settings_map_|.
  virtual std::vector<std::unique_ptr<Object>> GetAllGrantedObjects();

  // Grants |origin| access to |object| by writing it into
  // |host_content_settings_map_|.
  // TODO(https://crbug.com/1189682): Combine GrantObjectPermission and
  // UpdateObjectPermission methods into key-based GrantOrUpdateObjectPermission
  // once backend is updated to make key-based methods more efficient.
  void GrantObjectPermission(const url::Origin& origin, base::Value object);

  // Updates |old_object| with |new_object| for |origin|, and writes the value
  // into |host_content_settings_map_|.
  void UpdateObjectPermission(const url::Origin& origin,
                              const base::Value& old_object,
                              base::Value new_object);

  // Revokes |origin|'s permission to access |object|.
  //
  // This method may be extended by a subclass to revoke permission to access
  // objects returned by GetGrantedObjects but not stored in
  // |host_content_settings_map_|.
  // TODO(https://crbug.com/1189682): Remove this method once backend is updated
  // to make key-based methods more efficient.
  virtual void RevokeObjectPermission(const url::Origin& origin,
                                      const base::Value& object);

  // Revokes |origin|'s permission to access the object corresponding to |key|.
  // This method should only be called if |GetKeyForObject()| is overridden to
  // return sensible keys.
  //
  // This method may be extended by a subclass to revoke permission to access
  // objects returned by GetGrantedObjects but not stored in
  // |host_content_settings_map_|.
  virtual void RevokeObjectPermission(const url::Origin& origin,
                                      const base::StringPiece key);

  // Returns whether |origin| has granted objects.
  //
  // This method may be extended by a subclass to include permission to access
  // objects returned by GetGrantedObjects but not stored in
  // |host_content_settings_map_|.
  virtual bool HasGrantedObjects(const url::Origin& origin);

  // Returns a string which is used to uniquely identify this object.
  virtual std::string GetKeyForObject(const base::Value& object) = 0;

  // Validates the structure of an object read from
  // |host_content_settings_map_|.
  virtual bool IsValidObject(const base::Value& object) = 0;

  // Gets the human-readable name for a given object.
  virtual std::u16string GetObjectDisplayName(const base::Value& object) = 0;

  // Triggers the immediate flushing of all scheduled save setting operations.
  // To be called when the host_content_settings_map_ is about to become
  // unusable (e.g. browser context shutting down).
  void FlushScheduledSaveSettingsCalls();

 protected:
  // TODO(odejesush): Use this method in all derived classes instead of using a
  // member variable to store this state.
  bool IsOffTheRecord();
  void NotifyPermissionChanged();
  void NotifyPermissionRevoked(const url::Origin& origin);

  const absl::optional<ContentSettingsType> guard_content_settings_type_;
  const ContentSettingsType data_content_settings_type_;
  base::ObserverList<PermissionObserver> permission_observer_list_;

 private:
  base::Value::Dict GetWebsiteSetting(const url::Origin& origin,
                                      content_settings::SettingInfo* info);
  void SaveWebsiteSetting(const url::Origin& origin);
  void ScheduleSaveWebsiteSetting(const url::Origin& origin);
  virtual std::vector<std::unique_ptr<Object>> GetWebsiteSettingObjects();
  void LoadWebsiteSettingsIntoObjects();

  // Getter for `objects_` used to initialize the structure at first access.
  // Never use the `objects_` member directly outside of this function.
  ObjectMap& objects();

  const raw_ptr<HostContentSettingsMap> host_content_settings_map_;

  // In-memory cache that holds the granted objects. Lazy-initialized by first
  // call to `objects()`.
  ObjectMap objects_;

  // Whether the `objects_` member was initialized;
  bool objects_initialized_ = false;

  // Origins that have a scheduled `SaveWebsiteSetting` call.
  base::flat_set<url::Origin> origins_with_scheduled_save_settings_calls_;

  base::WeakPtrFactory<ObjectPermissionContextBase> weak_factory_{this};
};

}  // namespace permissions

#endif  // COMPONENTS_PERMISSIONS_OBJECT_PERMISSION_CONTEXT_BASE_H_