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

#include <memory>
#include <optional>

#include "base/functional/callback_forward.h"
#include "content/browser/renderer_host/navigation_type.h"
#include "content/public/browser/commit_deferring_condition.h"
#include "content/public/browser/frame_tree_node_id.h"

namespace base {
class TimeTicks;
}

namespace content {

class NavigationRequest;

// Updates URL of prerendered page if ones of initial and activation are
// different
//
// Background: A prerender can be triggered for a URL (e.g., `/?q=a`) and then
// activated by a navigation to a different but compatible URL (e.g.,
// `/?q=a&b=c`) if the server provides the `No-Vary-Search` HTTP header. In this
// case, the URL displayed to the user should be the latter.
//
// More precisely, there are two patterns that need to update the URL:
//
// - No-Vary-Search match: Prerender can have No-Vary-Search hint and header to
//   relax the matching condition.
//
//   https://httpwg.org/http-extensions/draft-ietf-httpbis-no-vary-search.html
//
//   Used in SpeculationRules, `SearchPreloadService`, etc.
// - Custom `PrerenderAttribute::url_match_predicate` (and
//   `PreloadingAttemptImpl::url_match_predicate_`)
//
//   See also `PrerenderHost::IsUrlMatch()` and a caller
//   `PrerenderHostRegistry::FindPotentialHostToActivate()`.
//
//   Used in `SearchPrefetchService`. (It uses `IsSearchDestinationMatch()`.)
//
// This class ensures the URL consistency. It defers the navigation commit and
// sends an IPC to the renderer process to update its internal URL. The commit
// resumes once the renderer process acknowledges the update. This condition is
// created only for prerender activations. If the prerendered page has already
// navigated to another URL, this class does nothing to respect the latest
// navigation.
//
// TODO(crbug.com/479980025): Rename it.
class PrerenderNoVarySearchCommitDeferringCondition
    : public CommitDeferringCondition {
 public:
  ~PrerenderNoVarySearchCommitDeferringCondition() override;
  static std::unique_ptr<CommitDeferringCondition> MaybeCreate(
      NavigationRequest& navigation_request,
      NavigationType navigation_type,
      std::optional<FrameTreeNodeId> candidate_prerender_frame_tree_node_id);

  // Called upon the renderer confirmed that the prerender URL has been updated.
  static void OnUrlUpdated(base::TimeTicks defer_start_time,
                           std::string histogram_suffix,
                           base::OnceClosure resume);

  Result WillCommitNavigation(base::OnceClosure resume) override;
  const char* TraceEventName() const override;

 private:
  PrerenderNoVarySearchCommitDeferringCondition(
      NavigationRequest& navigation_request,
      FrameTreeNodeId candidate_prerender_frame_tree_node_id);
  const FrameTreeNodeId candidate_prerender_frame_tree_node_id_;
};

}  // namespace content
#endif  // CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_NO_VARY_SEARCH_COMMIT_DEFERRING_CONDITION_H_
