// Copyright 2019 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_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_THROTTLE_H_
#define CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_THROTTLE_H_

#include <memory>
#include <set>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "chrome/browser/lookalikes/lookalike_url_blocking_page.h"
#include "components/url_formatter/url_formatter.h"
#include "content/public/browser/navigation_throttle.h"
#include "services/metrics/public/cpp/ukm_source_id.h"

namespace content {
class NavigationThrottleRegistry;
}  // namespace content

class Profile;

namespace lookalikes {
struct DomainInfo;
}

// Returns true if the redirect is deemed to be safe. These are generally
// defensive registrations where the domain owner redirects the IDN to the ASCII
// domain. See the unit tests for examples.
// In short, |url| must redirect to the root of |safe_url_host| or one
// of its subdomains.
bool IsSafeRedirect(const std::string& safe_url_host,
                    const std::vector<GURL>& redirect_chain);

// Observes navigations and shows an interstitial if the navigated domain name
// is visually similar to a top domain or a domain with a site engagement score.
//
// Remember to update //docs/idn.md with the appropriate information if you
// modify the lookalike heuristics.
//
// This throttle assumes that two navigations never share the same throttle.
class LookalikeUrlNavigationThrottle : public content::NavigationThrottle {
 public:
  explicit LookalikeUrlNavigationThrottle(
      content::NavigationThrottleRegistry& registry);
  ~LookalikeUrlNavigationThrottle() override;

  // content::NavigationThrottle:
  ThrottleCheckResult WillStartRequest() override;
  ThrottleCheckResult WillProcessResponse() override;
  ThrottleCheckResult WillRedirectRequest() override;
  const char* GetNameForLogging() override;

  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);

  // The throttle normally ignores testing profiles and returns PROCEED. This
  // function forces unit tests to not ignore them .
  void SetUseTestProfileForTesting() { use_test_profile_ = true; }

 private:
  // Performs synchronous top domain and engaged site checks on the navigated
  // and redirected urls. Uses |engaged_sites| for the engaged site checks.
  ThrottleCheckResult PerformChecks(
      const std::vector<lookalikes::DomainInfo>& engaged_sites);

  // A void-returning variant, only used with deferred throttle results (e.g.
  // when we need to fetch engaged sites list or digital asset link manifests).
  // |start| is the time at which the navigation was deferred, for metrics.
  void PerformChecksDeferred(
      base::TimeTicks start,
      const std::vector<lookalikes::DomainInfo>& engaged_sites);

  // Shows a full page interstitial. |safe_domain| is the domain suggested as
  // safe by the interstitial. |lookalike_domain| is the domain that triggered
  // the warning.
  // This function can display two types of interstitials depending on the
  // value of |safe_domain|:
  // - If |safe_domain| is a valid URL, it displays a lookalike interstitial
  // that suggests the user to go to |safe_domain| instead.
  // - Otherwise, it displays the punycode interstitial which doesn't suggest a
  // safe URL.
  ThrottleCheckResult ShowInterstitial(
      const GURL& safe_domain,
      const GURL& lookalike_domain,
      ukm::SourceId source_id,
      lookalikes::LookalikeUrlMatchType match_type,
      bool triggered_by_initial_url);

  // Checks if a full page intersitial can be shown. This function checks if
  // the navigation isn't a prerender navigation.
  ThrottleCheckResult CheckAndMaybeShowInterstitial(
      const GURL& safe_domain,
      const GURL& lookalike_domain,
      ukm::SourceId source_id,
      lookalikes::LookalikeUrlMatchType match_type,
      bool triggered_by_initial_url);

  // Kicks off a task to prewarm the lookalike checks for URLs in this
  // navigations redirect chain.
  void PrewarmLookalikeCheckAsync();

  // Synchronously runs the lookalike checks for this navigation's front of the
  // redirect chain and the current back of the redirect chain. Stores the
  // result in |lookalike_cache_|.
  void PrewarmLookalikeCheckSync();

  // Used as a callback, calls |PrewarmLookalikeCheckSync()|.
  void PrewarmLookalikeCheckSyncWithSites(
      const std::vector<lookalikes::DomainInfo>& engaged_sites);

  // Caches the lookalike check for |url| in |lookalike_cache_| if it has not
  // already been cached during the navigation.
  void PrewarmLookalikeCheckForURL(
      const GURL& url,
      const std::vector<lookalikes::DomainInfo>& engaged_sites);

  raw_ptr<Profile> profile_;
  bool use_test_profile_ = false;

  // Cached results from checking if a URL host is a lookalike. Also stores the
  // match type and suggested url. The lifetime of |this| is scoped to a single
  // navigation, which leaves the results of this check relatively fresh.
  std::map<std::string,
           std::tuple<bool, lookalikes::LookalikeUrlMatchType, GURL>>
      lookalike_cache_;

  // The number of lookalike async checks started from |WillRedirect()|. Used
  // to limit the number of attempts at caching the lookalike check.
  int redirect_lookup_cache_checks_ = 0;

  // Holds a cancellable callback to run |PrewarmLookalikeCheckAsync()|
  // asynchronously.
  base::OneShotTimer lookup_timer_;

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

#endif  // CHROME_BROWSER_LOOKALIKES_LOOKALIKE_URL_NAVIGATION_THROTTLE_H_
