File: prediction_service.cc

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 (196 lines) | stat: -rw-r--r-- 7,347 bytes parent folder | download | duplicates (6)
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
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/permissions/prediction_service/prediction_service.h"

#include <cmath>
#include <memory>
#include <utility>

#include "base/command_line.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "components/permissions/features.h"
#include "components/permissions/prediction_service/prediction_common.h"
#include "components/permissions/prediction_service/prediction_request_features.h"
#include "components/permissions/prediction_service/prediction_service_messages.pb.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/public/mojom/url_response_head.mojom.h"

namespace {

constexpr base::TimeDelta kURLLookupTimeout = base::Seconds(2);

net::NetworkTrafficAnnotationTag GetTrafficAnnotationTag() {
  return net::DefineNetworkTrafficAnnotation("permission_predictions", R"(
    semantics {
      sender: "Web Permission Perdictions"
      description:
        "A request to the Web Permission Predictions Service. The service will "
        "attempt to predict the likelihood that the user would grant this "
        "permission. Based on this prediction Chrome might decide to present "
        "the user with a different UI; a less intrusive one."
      trigger:
        "A permission prompt is about to be shown to the user, and the user "
        "has opted into Safe Browsing."
      data:
        "User stats helpful for attempting to predict the user's likelihood "
        "of granting the permission: the permission type, the presence of a "
        "user gesture, the user's OS, average deny/grant/ignore/dismiss rates "
        "and total prompts shown, both for the specific permission type and "
        "overall for all permission types."
      destination: GOOGLE_OWNED_SERVICE
    }
    policy {
      cookies_allowed: NO
      setting:
        "This can be disabled by disabling Safe Browsing by going to "
        "Settings and then to the Security sub-menu."
      chrome_policy {
        SafeBrowsingProtectionLevel {
          SafeBrowsingProtectionLevel: 0
        }
      }
    })");
}

}  // namespace

namespace permissions {

PredictionService::PredictionService(
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
    : url_loader_factory_(url_loader_factory) {}
PredictionService::~PredictionService() = default;

void PredictionService::StartLookup(const PredictionRequestFeatures& entity,
                                    LookupRequestCallback request_callback,
                                    LookupResponseCallback response_callback) {
  auto request = GetResourceRequest();
  auto proto_request = GetPredictionRequestProto(entity);
  std::string request_data;

  proto_request->SerializeToString(&request_data);

  SendRequestInternal(std::move(request), request_data, entity,
                      std::move(response_callback));

  if (request_callback)
    std::move(request_callback).Run(std::move(proto_request), std::string());
}

// static
const GURL PredictionService::GetPredictionServiceUrl(
    bool recalculate_for_testing) {
  static base::NoDestructor<GURL> default_prediction_service_url{
      kDefaultPredictionServiceUrl};
  static base::NoDestructor<GURL> command_line_url_override{
      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
          kDefaultPredictionServiceUrlSwitchKey)};

  // To facilitate tests that want to exercise various url building logic,
  // reinitialize the static variables if this flag is set.
  if (recalculate_for_testing) {
    *command_line_url_override =
        GURL(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
            kDefaultPredictionServiceUrlSwitchKey));
  }

  if (command_line_url_override->is_valid())
    return *command_line_url_override;

  return *default_prediction_service_url;
}

std::unique_ptr<network::ResourceRequest>
PredictionService::GetResourceRequest() {
  auto request = std::make_unique<network::ResourceRequest>();

  request->url =
      prediction_service_url_override_.is_empty()
          ? GetPredictionServiceUrl(recalculate_service_url_every_time)
          : prediction_service_url_override_;
  request->load_flags = net::LOAD_DISABLE_CACHE;
  request->method = "POST";
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;

  return request;
}

void PredictionService::SendRequestInternal(
    std::unique_ptr<network::ResourceRequest> request,
    const std::string& request_data,
    const PredictionRequestFeatures& entity,
    LookupResponseCallback response_callback) {
  std::unique_ptr<network::SimpleURLLoader> owned_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       GetTrafficAnnotationTag());
  owned_loader->AttachStringForUpload(request_data, "application/x-protobuf");

  owned_loader->SetTimeoutDuration(kURLLookupTimeout);
  owned_loader->DownloadToString(
      url_loader_factory_.get(),
      base::BindOnce(&PredictionService::OnURLLoaderComplete,
                     weak_factory_.GetWeakPtr(), entity, owned_loader.get(),
                     base::TimeTicks::Now()),
      network::SimpleURLLoader::kMaxBoundedStringDownloadSize);

  pending_requests_[std::move(owned_loader)] = std::move(response_callback);
}

void PredictionService::OnURLLoaderComplete(
    const PredictionRequestFeatures& entity,
    network::SimpleURLLoader* loader,
    base::TimeTicks request_start_time,
    std::unique_ptr<std::string> response_body) {
  for (auto& request : pending_requests_) {
    if (request.first.get() == loader) {
      auto prediction_response =
          CreatePredictionsResponse(loader, response_body.get());

      if (request.second) {
        std::optional<GeneratePredictionsResponse> response;
        if (prediction_response == nullptr) {
          response = std::nullopt;
        } else {
          response = *prediction_response;
        }
        bool lookup_success = prediction_response != nullptr;
        std::move(request.second)
            .Run(lookup_success, /*Response from cache=*/false, response);
      }

      pending_requests_.erase(request.first);
      return;
    }
  }

  NOTREACHED() << "Unexpected loader callback.";
}

std::unique_ptr<GeneratePredictionsResponse>
PredictionService::CreatePredictionsResponse(network::SimpleURLLoader* loader,
                                             const std::string* response_body) {
  if (!response_body || loader->NetError() != net::OK ||
      loader->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
    return nullptr;
  }

  auto predictions_response = std::make_unique<GeneratePredictionsResponse>();
  if (!predictions_response->ParseFromString(*response_body)) {
    return nullptr;
  }

  return predictions_response;
}

}  // namespace permissions