// Copyright 2015 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.

#ifndef IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IO_DATA_H_
#define IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IO_DATA_H_

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

#include "base/callback_forward.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/prefs/pref_member.h"
#include "ios/chrome/browser/ios_chrome_io_thread.h"
#include "ios/chrome/browser/net/net_types.h"
#include "net/cert/ct_verifier.h"
#include "net/cookies/cookie_monster.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_session.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_job_factory.h"

class HostContentSettingsMap;
class IOSChromeHttpUserAgentSettings;
class IOSChromeNetworkDelegate;
class IOSChromeURLRequestContextGetter;

namespace content_settings {
class CookieSettings;
}

namespace ios {
class ChromeBrowserState;
enum class ChromeBrowserStateType;
}

namespace net {
class CertificateReportSender;
class CertVerifier;
class ChannelIDService;
class CookieStore;
class HttpServerProperties;
class HttpTransactionFactory;
class ProxyConfigService;
class ProxyService;
class ServerBoundCertService;
class SSLConfigService;
class TransportSecurityPersister;
class TransportSecurityState;
class URLRequestJobFactoryImpl;
}  // namespace net

// Conceptually speaking, the ChromeBrowserStateIOData represents data that
// lives on the IO thread that is owned by a ChromeBrowserState, such as, but
// not limited to, network objects like CookieMonster, HttpTransactionFactory,
// etc.
// ChromeBrowserState owns ChromeBrowserStateIOData, but will make sure to
// delete it on the IO thread.
class ChromeBrowserStateIOData {
 public:
  typedef std::vector<scoped_refptr<IOSChromeURLRequestContextGetter>>
      IOSChromeURLRequestContextGetterVector;

  virtual ~ChromeBrowserStateIOData();

  // Returns true if |scheme| is handled in Chrome, or by default handlers in
  // net::URLRequest.
  static bool IsHandledProtocol(const std::string& scheme);

  // Utility to install additional WebUI handlers into the |job_factory|.
  // Ownership of the handlers is transfered from |protocol_handlers|
  // to the |job_factory|.
  static void InstallProtocolHandlers(
      net::URLRequestJobFactoryImpl* job_factory,
      ProtocolHandlerMap* protocol_handlers);

  // Initializes the ChromeBrowserStateIOData object and primes the
  // RequestContext generation. Must be called prior to any of the Get*()
  // methods other than GetResouceContext or GetMetricsEnabledStateOnIOThread.
  void Init(ProtocolHandlerMap* protocol_handlers) const;

  net::URLRequestContext* GetMainRequestContext() const;
  net::URLRequestContext* GetIsolatedAppRequestContext(
      net::URLRequestContext* main_context,
      const base::FilePath& partition_path) const;

  // Sets the cookie store associated with a partition path.
  // The path must exist. If there is already a cookie store, it is deleted.
  void SetCookieStoreForPartitionPath(scoped_ptr<net::CookieStore> cookie_store,
                                      const base::FilePath& partition_path);

  // These are useful when the Chrome layer is called from the content layer
  // with a content::ResourceContext, and they want access to Chrome data for
  // that browser state.
  content_settings::CookieSettings* GetCookieSettings() const;
  HostContentSettingsMap* GetHostContentSettingsMap() const;

  StringPrefMember* google_services_account_id() const {
    return &google_services_user_account_id_;
  }

  BooleanPrefMember* safe_browsing_enabled() const {
    return &safe_browsing_enabled_;
  }

  net::TransportSecurityState* transport_security_state() const {
    return transport_security_state_.get();
  }

  ios::ChromeBrowserStateType browser_state_type() const {
    return browser_state_type_;
  }

  bool IsOffTheRecord() const;

  // Initialize the member needed to track the metrics enabled state. This is
  // only to be called on the UI thread.
  void InitializeMetricsEnabledStateOnUIThread();

  // Returns whether or not metrics reporting is enabled in the browser instance
  // on which this browser state resides. This is safe for use from the IO
  // thread, and should only be called from there.
  bool GetMetricsEnabledStateOnIOThread() const;

 protected:
  // A URLRequestContext for apps that owns its cookie store and HTTP factory,
  // to ensure they are deleted.
  class AppRequestContext : public net::URLRequestContext {
   public:
    AppRequestContext();

    void SetCookieStore(scoped_ptr<net::CookieStore> cookie_store);
    void SetHttpTransactionFactory(
        scoped_ptr<net::HttpTransactionFactory> http_factory);
    void SetJobFactory(scoped_ptr<net::URLRequestJobFactory> job_factory);

    ~AppRequestContext() override;

   private:
    scoped_ptr<net::CookieStore> cookie_store_;
    scoped_ptr<net::HttpTransactionFactory> http_factory_;
    scoped_ptr<net::URLRequestJobFactory> job_factory_;
  };

  // Created on the UI thread, read on the IO thread during
  // ChromeBrowserStateIOData lazy initialization.
  struct ProfileParams {
    ProfileParams();
    ~ProfileParams();

    base::FilePath path;
    IOSChromeIOThread* io_thread;
    scoped_refptr<content_settings::CookieSettings> cookie_settings;
    scoped_refptr<HostContentSettingsMap> host_content_settings_map;
    scoped_refptr<net::SSLConfigService> ssl_config_service;
    scoped_refptr<net::CookieMonsterDelegate> cookie_monster_delegate;

    // We need to initialize the ProxyConfigService from the UI thread
    // because on linux it relies on initializing things through gconf,
    // and needs to be on the main thread.
    scoped_ptr<net::ProxyConfigService> proxy_config_service;

    // The browser state this struct was populated from. It's passed as a void*
    // to ensure it's not accidently used on the IO thread.
    void* browser_state;
  };

  explicit ChromeBrowserStateIOData(
      ios::ChromeBrowserStateType browser_state_type);

  void InitializeOnUIThread(ios::ChromeBrowserState* browser_state);
  void ApplyProfileParamsToContext(net::URLRequestContext* context) const;

  scoped_ptr<net::URLRequestJobFactory> SetUpJobFactoryDefaults(
      scoped_ptr<net::URLRequestJobFactoryImpl> job_factory,
      URLRequestInterceptorScopedVector request_interceptors,
      net::NetworkDelegate* network_delegate) const;

  // Called when the ChromeBrowserState is destroyed. |context_getters| must
  // include all URLRequestContextGetters that refer to the
  // ChromeBrowserStateIOData's URLRequestContexts. Triggers destruction of the
  // ChromeBrowserStateIOData and shuts down |context_getters| safely on the IO
  // thread.
  // TODO(mmenke):  Passing all those URLRequestContextGetters around like this
  //     is really silly.  Can we do something cleaner?
  void ShutdownOnUIThread(
      scoped_ptr<IOSChromeURLRequestContextGetterVector> context_getters);

  // A ChannelIDService object is created by a derived class of
  // ChromeBrowserStateIOData, and the derived class calls this method to set
  // the channel_id_service_ member and transfers ownership to the base class.
  void set_channel_id_service(net::ChannelIDService* channel_id_service) const;

  net::ProxyService* proxy_service() const { return proxy_service_.get(); }

  base::WeakPtr<net::HttpServerProperties> http_server_properties() const;

  void set_http_server_properties(
      scoped_ptr<net::HttpServerProperties> http_server_properties) const;

  net::URLRequestContext* main_request_context() const {
    return main_request_context_.get();
  }

  bool initialized() const { return initialized_; }

  scoped_ptr<net::HttpNetworkSession> CreateHttpNetworkSession(
      const ProfileParams& profile_params) const;

  // Creates main network transaction factory.
  scoped_ptr<net::HttpCache> CreateMainHttpFactory(
      net::HttpNetworkSession* session,
      scoped_ptr<net::HttpCache::BackendFactory> main_backend) const;

  // Creates network transaction factory.
  scoped_ptr<net::HttpCache> CreateHttpFactory(
      net::HttpNetworkSession* shared_session,
      scoped_ptr<net::HttpCache::BackendFactory> backend) const;

 private:
  typedef std::map<base::FilePath, AppRequestContext*> URLRequestContextMap;

  // --------------------------------------------
  // Virtual interface for subtypes to implement:
  // --------------------------------------------

  // Does the actual initialization of the ChromeBrowserStateIOData subtype.
  // Subtypes should use the static helper functions above to implement this.
  virtual void InitializeInternal(
      scoped_ptr<IOSChromeNetworkDelegate> chrome_network_delegate,
      ProfileParams* profile_params,
      ProtocolHandlerMap* protocol_handlers) const = 0;

  // Does an on-demand initialization of a RequestContext for the given
  // isolated app.
  virtual AppRequestContext* InitializeAppRequestContext(
      net::URLRequestContext* main_context) const = 0;

  // These functions are used to transfer ownership of the lazily initialized
  // context from ChromeBrowserStateIOData to the URLRequestContextGetter.
  virtual AppRequestContext* AcquireIsolatedAppRequestContext(
      net::URLRequestContext* main_context) const = 0;

  // The order *DOES* matter for the majority of these member variables, so
  // don't move them around unless you know what you're doing!
  // General rules:
  //   * ResourceContext references the URLRequestContexts, so
  //   URLRequestContexts must outlive ResourceContext, hence ResourceContext
  //   should be destroyed first.
  //   * URLRequestContexts reference a whole bunch of members, so
  //   URLRequestContext needs to be destroyed before them.
  //   * Therefore, ResourceContext should be listed last, and then the
  //   URLRequestContexts, and then the URLRequestContext members.
  //   * Note that URLRequestContext members have a directed dependency graph
  //   too, so they must themselves be ordered correctly.

  // Tracks whether or not we've been lazily initialized.
  mutable bool initialized_;

  // Data from the UI thread from the ChromeBrowserState, used to initialize
  // ChromeBrowserStateIOData. Deleted after lazy initialization.
  mutable scoped_ptr<ProfileParams> profile_params_;

  mutable StringPrefMember google_services_user_account_id_;

  // Member variables which are pointed to by the various context objects.
  mutable BooleanPrefMember enable_referrers_;
  mutable BooleanPrefMember enable_do_not_track_;
  mutable BooleanPrefMember safe_browsing_enabled_;
  mutable BooleanPrefMember sync_disabled_;
  mutable BooleanPrefMember signin_allowed_;

  BooleanPrefMember enable_metrics_;

  // Pointed to by URLRequestContext.
  mutable scoped_ptr<net::ChannelIDService> channel_id_service_;

  mutable scoped_ptr<net::ProxyService> proxy_service_;
  mutable scoped_ptr<net::TransportSecurityState> transport_security_state_;
  mutable scoped_ptr<net::CTVerifier> cert_transparency_verifier_;
  mutable scoped_ptr<net::HttpServerProperties> http_server_properties_;
  mutable scoped_ptr<net::TransportSecurityPersister>
      transport_security_persister_;
  mutable scoped_ptr<net::CertificateReportSender> certificate_report_sender_;

  // These are only valid in between LazyInitialize() and their accessor being
  // called.
  mutable scoped_ptr<net::URLRequestContext> main_request_context_;
  // One URLRequestContext per isolated app for main and media requests.
  mutable URLRequestContextMap app_request_context_map_;

  mutable scoped_refptr<content_settings::CookieSettings> cookie_settings_;

  mutable scoped_refptr<HostContentSettingsMap> host_content_settings_map_;

  mutable scoped_ptr<IOSChromeHttpUserAgentSettings>
      chrome_http_user_agent_settings_;

  // TODO(jhawkins): Remove once crbug.com/102004 is fixed.
  bool initialized_on_UI_thread_;

  const ios::ChromeBrowserStateType browser_state_type_;

  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserStateIOData);
};

#endif  // IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IO_DATA_H_