// Copyright 2012 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_OMNIBOX_BROWSER_IN_MEMORY_URL_INDEX_H_
#define COMPONENTS_OMNIBOX_BROWSER_IN_MEMORY_URL_INDEX_H_

#include <stddef.h>

#include <functional>
#include <set>
#include <string>

#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/threading/thread_checker.h"
#include "base/trace_event/memory_dump_provider.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/history/core/browser/history_types.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/omnibox/browser/scored_history_match.h"
#include "components/search_engines/template_url_service.h"

class FakeAutocompleteProviderClient;
class HistoryQuickProviderTest;
class OmniboxTriggeredFeatureService;

namespace base {
class SequencedTaskRunner;
}

namespace bookmarks {
class BookmarkModel;
}

namespace history {
class HistoryDatabase;
class HQPPerfTestOnePopularURL;
}  // namespace history

class URLIndexPrivateData;

typedef std::set<std::string> SchemeSet;

// The URL history source.
// Holds portions of the URL database in memory in an indexed form.  Used to
// quickly look up matching URLs for a given query string.  Used by
// the HistoryQuickProvider for inline autocomplete and to provide URL
// matches to the omnibox.
//
// Note about multi-byte codepoints and the data structures in the
// InMemoryURLIndex class: One will quickly notice that no effort is made to
// insure that multi-byte character boundaries are detected when indexing the
// words and characters in the URL history database except when converting
// URL strings to lowercase. Multi-byte-edness makes no difference when
// indexing or when searching the index as the final filtering of results
// is dependent on the comparison of a string of bytes, not individual
// characters. While the lookup of those bytes during a search in the
// |char_word_map_| could serve up words in which the individual char16
// occurs as a portion of a composite character the next filtering step
// will eliminate such words except in the case where a single character
// is being searched on and which character occurs as the second char16 of a
// multi-char16 instance.
class InMemoryURLIndex : public KeyedService,
                         public history::HistoryServiceObserver,
                         public base::trace_event::MemoryDumpProvider {
 public:
  // `history_service` may be null during unit testing.
  InMemoryURLIndex(bookmarks::BookmarkModel* bookmark_model,
                   history::HistoryService* history_service,
                   TemplateURLService* template_url_service,
                   const base::FilePath& history_dir,
                   const SchemeSet& client_schemes_to_allowlist);
  ~InMemoryURLIndex() override;
  InMemoryURLIndex(const InMemoryURLIndex&) = delete;
  InMemoryURLIndex& operator=(const InMemoryURLIndex&) = delete;

  // Opens and prepares the index of historical URL visits. Rebuilds the index
  // from History.
  void Init();

  // Scans the history index and returns a vector with all scored, matching
  // history items. This entry point simply forwards the call to
  // `URLIndexPrivateData`. For a complete description of this function refer to
  // that class. If `cursor_position` is `npos`, the function doesn't do
  // anything special with the cursor; this is equivalent to the cursor being at
  // the end. If `host_filter` is not empty, filters matches by host. In total,
  // `max_matches` of items will be returned.
  ScoredHistoryMatches HistoryItemsForTerms(
      const std::u16string& term_string,
      size_t cursor_position,
      size_t max_matches,
      OmniboxTriggeredFeatureService* triggered_feature_service);

  // Deletes the index entry, if any, for the given |url|.
  void DeleteURL(const GURL& url);

  // Indicates that the index restoration is complete.
  bool restored() const { return restored_; }

 private:
  friend class ::FakeAutocompleteProviderClient;
  friend class ::HistoryQuickProviderTest;
  friend class history::HQPPerfTestOnePopularURL;
  friend class InMemoryURLIndexTest;
  FRIEND_TEST_ALL_PREFIXES(InMemoryURLIndexTest, ExpireRow);
  FRIEND_TEST_ALL_PREFIXES(InMemoryURLIndexTest, OnURLVisited);
  FRIEND_TEST_ALL_PREFIXES(LimitedInMemoryURLIndexTest, Initialization);

  // HistoryDBTask used to rebuild our private data from the history database.
  class RebuildPrivateDataFromHistoryDBTask : public history::HistoryDBTask {
   public:
    explicit RebuildPrivateDataFromHistoryDBTask(
        base::WeakPtr<InMemoryURLIndex> index,
        const SchemeSet& scheme_allowlist);
    RebuildPrivateDataFromHistoryDBTask(
        const RebuildPrivateDataFromHistoryDBTask&) = delete;
    RebuildPrivateDataFromHistoryDBTask& operator=(
        const RebuildPrivateDataFromHistoryDBTask&) = delete;
    ~RebuildPrivateDataFromHistoryDBTask() override;

    bool RunOnDBThread(history::HistoryBackend* backend,
                       history::HistoryDatabase* db) override;
    void DoneRunOnMainThread() override;

   private:
    base::WeakPtr<InMemoryURLIndex>
        index_;                   // Call back to this index at completion.
    SchemeSet scheme_allowlist_;  // Schemes to be indexed.
    bool succeeded_ = false;      // Indicates if the rebuild was successful.
    scoped_refptr<URLIndexPrivateData> data_;  // The rebuilt private data.
  };

  // Clears the in-memory cache entirely. Called when History is cleared.
  void ClearPrivateData();

  // Schedules a history task to rebuild our private data from the history
  // database.
  void ScheduleRebuildFromHistory();

  // Callback used by RebuildPrivateDataFromHistoryDBTask to signal completion
  // or rebuilding our private data from the history database. |succeeded|
  // will be true if the rebuild was successful. |data| will point to a new
  // instanceof the private data just rebuilt.
  void DoneRebuildingPrivateDataFromHistoryDB(
      bool succeeded,
      scoped_refptr<URLIndexPrivateData> private_data);

  // KeyedService:
  // Signals that any outstanding initialization should be canceled.
  void Shutdown() override;

  // HistoryServiceObserver:
  void OnURLVisited(history::HistoryService* history_service,
                    const history::VisitedURLInfo& visited_url_info) override;
  void OnURLsModified(history::HistoryService* history_service,
                      const history::URLRows& changed_urls) override;
  void OnHistoryDeletions(history::HistoryService* history_service,
                          const history::DeletionInfo& deletion_info) override;

  // MemoryDumpProvider:
  bool OnMemoryDump(
      const base::trace_event::MemoryDumpArgs& args,
      base::trace_event::ProcessMemoryDump* process_memory_dump) override;

  // Returns a pointer to our private data. For unit testing only.
  URLIndexPrivateData* private_data() { return private_data_.get(); }

  // Returns a pointer to our private data cancelable request tracker. For
  // unit testing only.
  base::CancelableTaskTracker* private_data_tracker() {
    return &private_data_tracker_;
  }

  // Returns the set of allowlisted schemes. For unit testing only.
  const SchemeSet& scheme_allowlist() { return scheme_allowlist_; }

  // The BookmarkModel; may be null when testing.
  raw_ptr<bookmarks::BookmarkModel> bookmark_model_;

  // The HistoryService; may be null when testing.
  raw_ptr<history::HistoryService> history_service_;

  // The TemplateURLService; may be null when testing.  Used to identify URLs
  // that are from the default search provider.
  raw_ptr<TemplateURLService> template_url_service_;

  // Only URLs with a allowlisted scheme are indexed.
  SchemeSet scheme_allowlist_;

  // The index's durable private data.
  scoped_refptr<URLIndexPrivateData> private_data_;

  // Task runner used for operations which require disk access.
  const scoped_refptr<base::SequencedTaskRunner> task_runner_;

  base::CancelableTaskTracker private_data_tracker_;

  // Set to true once the shutdown process has begun.
  bool shutdown_ = false;

  // Set to true once the index restoration is complete.
  bool restored_ = false;

  base::ScopedObservation<history::HistoryService,
                          history::HistoryServiceObserver>
      history_service_observation_{this};

  base::ThreadChecker thread_checker_;
  base::WeakPtrFactory<InMemoryURLIndex> weak_ptr_factory_{this};
};

#endif  // COMPONENTS_OMNIBOX_BROWSER_IN_MEMORY_URL_INDEX_H_
