File: enterprise_search_aggregator_provider.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (303 lines) | stat: -rw-r--r-- 12,977 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
// Copyright 2025 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_ENTERPRISE_SEARCH_AGGREGATOR_PROVIDER_H_
#define COMPONENTS_OMNIBOX_BROWSER_ENTERPRISE_SEARCH_AGGREGATOR_PROVIDER_H_

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

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "components/omnibox/browser/autocomplete_enums.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_provider.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_provider_debouncer.h"
#include "services/data_decoder/public/cpp/data_decoder.h"

namespace network {
class SimpleURLLoader;
}

class AutocompleteInput;
class AutocompleteProviderClient;
class AutocompleteProviderDebouncer;
class AutocompleteProviderListener;
class TemplateURL;
class TemplateURLService;

class EnterpriseSearchAggregatorProvider : public AutocompleteProvider {
 public:
  using SuggestionType = AutocompleteMatch::EnterpriseSearchAggregatorType;

  // Relevance along with info for `AutocompleteMatch::additional_info`.
  struct RelevanceData {
    int relevance;
    size_t strong_word_matches;
    size_t weak_word_matches;
    std::string source;
  };

  EnterpriseSearchAggregatorProvider(AutocompleteProviderClient* client,
                                     AutocompleteProviderListener* listener);

  // AutocompleteProvider:
  void Start(const AutocompleteInput& input, bool minimal_changes) override;
  void Stop(AutocompleteStopReason stop_reason) override;

 private:
  friend class FakeEnterpriseSearchAggregatorProvider;

  // Tracks state for `Request` declared below.
  enum class RequestState {
    kNotStarted,
    kStarted,
    kCompleted,
  };

  // When parsing response JSONs, we need to track not only the parsed matches
  // but also how many results the response included. We can't simply use
  // `matches.size()`, because some response results might be filtered out.
  // `RequestParsed` is a helper to track those 2.
  struct RequestParsed {
    RequestParsed();
    RequestParsed(std::vector<AutocompleteMatch> matches, size_t result_count);
    RequestParsed(RequestParsed&&) noexcept;
    RequestParsed(const RequestParsed&) = delete;
    ~RequestParsed();
    RequestParsed& operator=(RequestParsed&&) noexcept;
    RequestParsed& operator=(const RequestParsed&) = delete;

    // When `multiple_requests` is false, the single request will be parsed in 3
    // calls to `ParseResultList()`. `Append()` simply merges the `matches` and
    // sums the `result_count`s.
    void Append(RequestParsed parsed);

    std::vector<AutocompleteMatch> matches;
    size_t result_count = 0;
  };

  // The provider makes multiple async requests in parallel. This helper handles
  // the callbacks, logging, and caching for each request.
  class Request {
   public:
    explicit Request(std::vector<SuggestionType> types);
    ~Request();
    Request(Request&&);
    Request(const Request&) = delete;

    // Whether this request should be made. E.g. query requests are not allowed
    // unscoped.
    bool Allowed(bool in_keyword_mode) const;
    // Clears most state. Conditionally doesn't clear `matches` in order to
    // support caching. `Reset(false)` should be called before `OnStart()` is
    // called. `Reset(true)` will immediately clear cached matches; it should
    // only be called if the request will not be started and completed; e.g.
    // unscoped query request. Will log the previous request if it's being
    // interrupted; i.e. `OnCompleted()` hadn't been called.
    void Reset(bool clear_cached_matches);
    // Called when the real request starts; after the auth request completes.
    // Should be called before `OnCompleted()` is called.
    void OnStart(std::unique_ptr<network::SimpleURLLoader> loader);
    // Called when the real request completes. Will replace cached matches and
    // log.
    void OnCompleted(RequestParsed parsed);

    // Logs how long it has been since a request started at `start_time`. Sliced
    // by request type and completion.
    static void LogResponseTime(const std::string& type_histogram_suffix,
                                bool interrupted,
                                base::TimeTicks start_time);
    // Logs how many results a response contained. Sliced by request type. Not
    // logged for interrupted requests.
    static void LogResultCount(const std::string& type_histogram_suffix,
                               int count);

    const std::vector<SuggestionType> Types() const;
    // Map `types_` to the integers the backend understands. Some types like
    // `CONTENT` map to multiple backend types. And `types_` itself is a vector
    // Hence this returns a vector.
    std::vector<int> BackendSuggestionTypes() const;
    RequestState State() const;
    base::TimeTicks StartTime() const;
    const std::vector<AutocompleteMatch>& Matches() const;
    int ResultCount() const;

   private:
    // Log all of this request's metrics on completion or interruption; i.e.
    // response time and result count.
    void Log(bool interrupted) const;
    // Map `types_` to a histogram suffix for slicing.
    std::string TypeHistogramSuffix() const;

    // The type of suggestions to request.
    // TODO(manukh): After launching multiple_requests, this can be a single
    //   value instead of a vector.
    const std::vector<SuggestionType> types_;
    // State of request. `start_time_` and `loader_` can't differentiate between
    // `kNotStarted` and `kCompleted`, so this explicit `state_` is necessary.
    RequestState state_ = RequestState::kCompleted;
    // Start time of ongoing request. Null before requests start and after they
    // complete.
    base::TimeTicks start_time_;
    // Not null for ongoing requests. Null before requests start and after they
    // complete.
    std::unique_ptr<network::SimpleURLLoader> loader_;
    RequestParsed parsed_;
  };

  ~EnterpriseSearchAggregatorProvider() override;

  // Determines whether the profile/session/window meet the feature
  // prerequisites.
  bool IsProviderAllowed(const AutocompleteInput& input);

  // Called by `debouncer_`, queued when `Start()` is called.
  void Run();

  // Callback for when the loader is available with a valid token. Takes
  // ownership of the loader.
  void RequestStarted(int request_index,
                      std::unique_ptr<network::SimpleURLLoader> loader);

  // Called when the network request for suggestions has completed.
  // `request_index` corresponds to the type of request sent:
  void RequestCompleted(int request_index,
                        const network::SimpleURLLoader* source,
                        int response_code,
                        std::unique_ptr<std::string> response_body);

  // Callback for parsing the response JSON string into `base::Value` in an
  // isolated process.
  void OnJsonParsedIsolated(int request_index,
                            base::expected<base::Value, std::string> result);

  // Called after parsing the response JSON string into `base::Value`, either in
  // the main or an isolated process. Will use the `Parse*()` methods below to
  // further parse the `base::Value` into `RequestParsed` and update
  // `requests[request_index]` with the `RequestParsed.
  void HandleParsedJson(int request_index,
                        const std::optional<base::Value::Dict>& response_value);

  // Parses the response `base::Value` into `RequestParsed`.
  RequestParsed ParseEnterpriseSearchAggregatorSearchResults(
      const std::vector<SuggestionType>& suggestion_types,
      const base::Value::Dict& root_val);

  // Helper method to parse query, people, and content suggestions.
  // - `input_words` is used for scoring matches.
  // - `suggestion_type` is used for selecting which JSON fields to look for,
  // scoring matches, and creating the match.
  // - `is_navigation` is used for creating the match.
  // Example:
  //   Given a `results` with one query suggestion:
  //    {
  //     "querySuggestions": [{
  //       "suggestion": "hello",
  //       "dataStore": [project/1]
  //      }]
  //     }.
  // `matches` would contain one `match` with the following properties:
  //  - `match.type` = `AutocompleteMatchType::SEARCH_SUGGEST`,
  //  - `match.contents` = "hello",
  //  - `match.description` = "",
  //  - `match.destination_url` = `template_url->url()`,
  //  - `match.fill_to_edit` = `template_url->url()`,
  //  - `match.image_url` = `icon_url` from EnterpriseSearchAggregatorSettings
  //  policy,
  //  - `match.relevance` = 1001.
  RequestParsed ParseResultList(std::set<std::u16string> input_words,
                                const base::Value::List* results,
                                SuggestionType suggestion_type,
                                bool is_navigation);

  // Helper method to get `destination_url` based on `suggestion_type` for
  // `CreateMatch()`.
  std::string GetMatchDestinationUrl(const base::Value::Dict& result,
                                     SuggestionType suggestion_type) const;

  // Helper method to get `description` based on `suggestion_type` for
  // `CreateMatch()`.
  std::string GetMatchDescription(const base::Value::Dict& result,
                                  SuggestionType suggestion_type) const;

  // Helper method to get `contents` based on `suggestion_type` for
  // `CreateMatch()`.
  std::string GetMatchContents(const base::Value::Dict& result,
                               SuggestionType suggestion_type) const;

  // Helper method to get a localized metadata string depending on which of
  // `update_time`, `owner`, and `content_type_description` exist.
  std::u16string GetLocalizedContentMetadata(
      const std::u16string& update_time,
      const std::u16string& owner,
      const std::u16string& content_type_description) const;

  // Helper method to get user-readable (e.g. 'chromium is awesome
  // document') fields that can be used to compare input similarity.
  // Non-user-readable fields (e.g. 'doc_id=123/locations/global') should be
  // excluded because the input matching that would be a coincidence and not
  // a sign the user wanted this suggestion. `GetMatchDescription()` and
  // `GetMatchContents()`, and `GetEmailUsernames()` should be passed to
  // `GetStrongScoringFields()`.
  std::vector<std::string> GetStrongScoringFields(
      const base::Value::Dict& result,
      SuggestionType suggestion_type,
      const std::string& contents,
      const std::string& description,
      const std::vector<std::u16string> email_usernames) const;
  std::vector<std::string> GetWeakScoringFields(
      const base::Value::Dict& result,
      SuggestionType suggestion_type) const;

  // Helper to create a match.
  AutocompleteMatch CreateMatch(SuggestionType suggestion_type,
                                bool is_navigation,
                                RelevanceData relevance_data,
                                const std::string& destination_url,
                                const std::string& image_url,
                                const std::string& icon_url,
                                const std::u16string& description,
                                const std::u16string& contents,
                                const std::u16string& fill_into_edit);

  // Aggregates the `RequestParsed`s of `requests_` into `matches_` and notifies
  // the provider listener. Called multiple times in each autocomplete pass;
  // once immediately when `Run()` is called to display the cached matches; then
  // again as each request completes.
  void AggregateMatches();

  // Called when all `requests_` complete or are interrupted.
  void LogAllRequests(bool interrupted);

  // Owned by AutocompleteController.
  const raw_ptr<AutocompleteProviderClient> client_;

  // Used to ensure that we don't send multiple requests in quick succession.
  std::unique_ptr<AutocompleteProviderDebouncer> debouncer_;

  // Saved when starting a new autocomplete request so that they can be
  // retrieved when responses return asynchronously.
  AutocompleteInput adjusted_input_;
  raw_ptr<const TemplateURL> template_url_;

  raw_ptr<TemplateURLService> template_url_service_;

  // See comment for `Request`. `requests_` are initialized in the provider
  // constructor and reused throughout the provider's lifetime. This is
  // necessary to cache results beyond a single autocomplete pass.
  std::vector<Request> requests_;

  base::WeakPtrFactory<EnterpriseSearchAggregatorProvider> weak_ptr_factory_{
      this};
};

#endif  // COMPONENTS_OMNIBOX_BROWSER_ENTERPRISE_SEARCH_AGGREGATOR_PROVIDER_H_