910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_FLOATING_SSO_FLOATING_SSO_SERVICE_H_
#define CHROME_BROWSER_ASH_FLOATING_SSO_FLOATING_SSO_SERVICE_H_

#include <memory>

#include "base/containers/flat_set.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/floating_sso/floating_sso_sync_bridge.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/sync/model/data_type_store.h"
#include "components/url_matcher/url_matcher.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_access_result.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"

namespace syncer {
class DataTypeControllerDelegate;
}  // namespace syncer

class PrefService;

namespace ash::floating_sso {

class FloatingSsoService : public KeyedService,
                           public network::mojom::CookieChangeListener,
                           public FloatingSsoSyncBridge::Observer {
 public:
  // Callback used to get a CookieManager.
  using CookieManagerGetter =
      base::RepeatingCallback<network::mojom::CookieManager*()>;

  FloatingSsoService(PrefService* prefs,
                     std::unique_ptr<FloatingSsoSyncBridge> bridge,
                     CookieManagerGetter cookie_manager_getter);
  FloatingSsoService(const FloatingSsoService&) = delete;
  FloatingSsoService& operator=(const FloatingSsoService&) = delete;

  ~FloatingSsoService() override;

  // KeyedService:
  void Shutdown() override;

  // network::mojom::CookieChangeListener:
  void OnCookieChange(const net::CookieChangeInfo& change) override;

  // FloatingSsoSyncBridge::Observer:
  void OnCookiesAddedOrUpdatedRemotely(
      const std::vector<net::CanonicalCookie>& cookies) override;
  void OnCookiesRemovedRemotely(
      const std::vector<net::CanonicalCookie>& cookies) override;

  bool IsFloatingSsoEnabled();
  // `callback` will be run once there are no cookie manager changes in
  // progress. This can be called repeatedly but only the latest callback will
  // be executed.
  void RunWhenCookiesAreReady(base::OnceClosure callback);
  // `callback` will be run once the bridge executes `MergeFullSyncData` method
  // (which can only happen when Sync is being enabled for the first time on
  // this client) and once conditions for `RunWhenCookiesAreReady` are also
  // satisfied.
  void RunWhenCookiesAreReadyOnFirstSync(base::OnceClosure callback);
  base::WeakPtr<syncer::DataTypeControllerDelegate> GetControllerDelegate();

  // Signal that this cookie shouldn't be overridden with remote
  // entities on initial Sync cycle.
  void MarkToNotOverride(const net::CanonicalCookie& cookie);

  bool ShouldSyncCookiesForUrl(const GURL& url) const;

  FloatingSsoSyncBridge* GetBridgeForTesting() { return bridge_.get(); }
  bool IsBoundToCookieManagerForTesting() { return receiver_.is_bound(); }

 private:
  void RegisterPolicyListeners();

  // Map the FloatingSsoDomainBlocklist and FloatingSsoDomainBlocklistExceptions
  // policies to URL matchers.
  void UpdateUrlMatchers();

  // Check if the feature is enabled based on the corresponding enterprise
  // policy. If yes, start observing cookies and uploading them to Sync, and
  // apply cookies from Sync if needed. If not, stop all of the above.
  void StartOrStop();

  void MaybeStartListening();
  void StopListening();
  void BindToCookieManager();
  void OnCookiesLoaded(const net::CookieList& cookies);
  bool ShouldSyncCookie(const net::CanonicalCookie& cookie) const;
  bool ShouldSyncSessionCookies() const;
  void OnConnectionError();
  bool IsDomainAllowed(const GURL& url) const;
  void OnCookieSet(net::CookieAccessResult result);
  void OnCookieDeleted(bool success);
  void DecrementChangesCountAndMaybeNotify();

  raw_ptr<PrefService> prefs_ = nullptr;
  // TODO(crbug.com/378091718): investigated lifetime issues when using
  // raw_ptr<network::mojom::CookieManager> instead of a callback here.
  CookieManagerGetter cookie_manager_getter_;
  std::unique_ptr<FloatingSsoSyncBridge> bridge_;
  base::ScopedObservation<FloatingSsoSyncBridge,
                          FloatingSsoSyncBridge::Observer>
      scoped_observation_{this};
  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;

  // We fetch the accumulated cookies the first time the service is started, as
  // well when the service stops listening and resumes due to setting changes.
  // We do not fetch accumulated cookies when the connection to the cookie
  // manager is disrupted because we attempt to reconnect right away.
  bool fetch_accumulated_cookies_ = true;

  // Count of changes (additions or deletions of cookies) currently being
  // performed by `cookie_manager_`.
  // TODO(crbug.com/377471962): Add explicit tests for this counter and for
  // `on_no_changes_in_progress_callback_`.
  int changes_in_progress_count_ = 0;

  base::OnceClosure on_no_changes_in_progress_callback_;

  mojo::Receiver<network::mojom::CookieChangeListener> receiver_{this};

  std::unique_ptr<url_matcher::URLMatcher> block_url_matcher_;
  std::unique_ptr<url_matcher::URLMatcher> except_url_matcher_;
};

}  // namespace ash::floating_sso

#endif  // CHROME_BROWSER_ASH_FLOATING_SSO_FLOATING_SSO_SERVICE_H_