Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1301)

Side by Side Diff: chrome/browser/safe_browsing/notification_image_reporter.cc

Issue 2637153002: Submit a sample of notification images to Safe Browsing (Closed)
Patch Set: nits Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/safe_browsing/notification_image_reporter.h"
6
7 #include <cmath>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/feature_list.h"
12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/rand_util.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
21 #include "chrome/common/safe_browsing/csd.pb.h"
22 #include "components/safe_browsing_db/database_manager.h"
23 #include "components/safe_browsing_db/safe_browsing_prefs.h"
24 #include "components/variations/variations_associated_data.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "net/base/net_errors.h"
27 #include "net/url_request/report_sender.h"
28 #include "skia/ext/image_operations.h"
29 #include "third_party/skia/include/core/SkBitmap.h"
30 #include "ui/gfx/codec/png_codec.h"
31 #include "ui/gfx/geometry/size.h"
32 #include "url/gurl.h"
33
34 using content::BrowserThread;
35
36 namespace safe_browsing {
37
38 namespace {
39
40 const size_t kMaxReportsPerDay = 5;
41 const base::Feature kNotificationImageReporterFeature{
42 "NotificationImageReporterFeature", base::FEATURE_ENABLED_BY_DEFAULT};
43 const char kReportChance[] = "ReportChance";
44 const char kDefaultMimeType[] = "image/png";
45
46 // Passed to ReportSender::Send as an ErrorCallback, so must take a GURL, but it
47 // is unused.
48 void LogReportResult(const GURL& url, int net_error) {
49 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.NotificationImageReporter.NetError",
50 net_error);
51 }
52
53 } // namespace
54
55 const char NotificationImageReporter::kReportingUploadUrl[] =
56 "https://safebrowsing.googleusercontent.com/safebrowsing/clientreport/"
57 "notification-image";
58
59 NotificationImageReporter::NotificationImageReporter(
60 net::URLRequestContext* request_context)
61 : NotificationImageReporter(base::MakeUnique<net::ReportSender>(
62 request_context,
63 net::ReportSender::CookiesPreference::DO_NOT_SEND_COOKIES)) {}
64
65 NotificationImageReporter::NotificationImageReporter(
66 std::unique_ptr<net::ReportSender> report_sender)
67 : report_sender_(std::move(report_sender)), weak_factory_on_io_(this) {
68 DCHECK_CURRENTLY_ON(BrowserThread::IO);
69 }
70
71 NotificationImageReporter::~NotificationImageReporter() {
72 DCHECK_CURRENTLY_ON(BrowserThread::IO);
73 }
74
75 void NotificationImageReporter::ReportNotificationImageOnIO(
76 Profile* profile,
77 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
78 const GURL& origin,
79 const SkBitmap& image) {
80 DCHECK_CURRENTLY_ON(BrowserThread::IO);
81 DCHECK(profile);
82 DCHECK_EQ(origin, origin.GetOrigin());
83 DCHECK(origin.is_valid());
84
85 // Skip whitelisted origins to cut down on report volume.
86 if (!database_manager || database_manager->MatchCsdWhitelistUrl(origin)) {
87 SkippedReporting();
88 return;
89 }
90
91 // Sample a Finch-controlled fraction only.
92 double report_chance = GetReportChance();
93 if (base::RandDouble() >= report_chance) {
94 SkippedReporting();
95 return;
96 }
97
98 // Avoid exceeding kMaxReportsPerDay.
99 base::Time a_day_ago = base::Time::Now() - base::TimeDelta::FromDays(1);
100 while (!report_times_.empty() &&
101 report_times_.front() < /* older than */ a_day_ago) {
102 report_times_.pop();
103 }
104 if (report_times_.size() >= kMaxReportsPerDay) {
105 SkippedReporting();
106 return;
107 }
108 // n.b. we write to report_times_ here even if we'll later end up skipping
109 // reporting because GetExtendedReportingLevel was not SBER_LEVEL_SCOUT. That
110 // saves us two thread hops, with the downside that we may underreport
111 // notifications on the first day that a user opts in to SBER_LEVEL_SCOUT.
112 report_times_.push(base::Time::Now());
113
114 BrowserThread::PostTask(
115 BrowserThread::UI, FROM_HERE,
116 base::Bind(&NotificationImageReporter::ReportNotificationImageOnUI,
117 weak_factory_on_io_.GetWeakPtr(), profile, origin, image));
118 }
119
120 double NotificationImageReporter::GetReportChance() const {
121 // Get the report_chance from the Finch experiment. If there is no active
122 // experiment, it will be set to the default of 0.
123 double report_chance = variations::GetVariationParamByFeatureAsDouble(
124 kNotificationImageReporterFeature, kReportChance, 0.0);
125
126 if (report_chance < 0.0 || report_chance > 1.0) {
127 DLOG(WARNING) << "Illegal value " << report_chance << " for the parameter "
128 << kReportChance << ". The value should be between 0 and 1.";
129 report_chance = 0.0;
130 }
131
132 return report_chance;
133 }
134
135 void NotificationImageReporter::SkippedReporting() {}
136
137 // static
138 void NotificationImageReporter::ReportNotificationImageOnUI(
139 const base::WeakPtr<NotificationImageReporter>& weak_this_on_io,
140 Profile* profile,
141 const GURL& origin,
142 const SkBitmap& image) {
143 DCHECK_CURRENTLY_ON(BrowserThread::UI);
144
145 // Skip reporting unless SBER2 Scout is enabled.
146 if (GetExtendedReportingLevel(*profile->GetPrefs()) != SBER_LEVEL_SCOUT) {
147 BrowserThread::PostTask(
148 BrowserThread::IO, FROM_HERE,
149 base::Bind(&NotificationImageReporter::SkippedReporting,
150 weak_this_on_io));
151 return;
152 }
153
154 BrowserThread::GetBlockingPool()->PostWorkerTask(
155 FROM_HERE,
156 base::Bind(
157 &NotificationImageReporter::DownscaleNotificationImageOnBlockingPool,
158 weak_this_on_io, origin, image));
159 }
160
161 // static
162 void NotificationImageReporter::DownscaleNotificationImageOnBlockingPool(
163 const base::WeakPtr<NotificationImageReporter>& weak_this_on_io,
164 const GURL& origin,
165 const SkBitmap& image) {
166 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
167
168 // Downscale to fit within 512x512. TODO(johnme): Get this from Finch.
169 const double MAX_SIZE = 512;
170 SkBitmap downscaled_image = image;
171 if ((image.width() > MAX_SIZE || image.height() > MAX_SIZE) &&
172 image.width() > 0 && image.height() > 0) {
173 double scale =
174 std::min(MAX_SIZE / image.width(), MAX_SIZE / image.height());
175 downscaled_image =
176 skia::ImageOperations::Resize(image, skia::ImageOperations::RESIZE_GOOD,
177 std::lround(scale * image.width()),
178 std::lround(scale * image.height()));
179 }
180
181 // Encode as PNG.
182 std::vector<unsigned char> png_bytes;
183 if (!gfx::PNGCodec::EncodeBGRASkBitmap(downscaled_image, false, &png_bytes)) {
184 NOTREACHED();
185 return;
186 }
187
188 BrowserThread::PostTask(
189 BrowserThread::IO, FROM_HERE,
190 base::Bind(&NotificationImageReporter::SendReportOnIO, weak_this_on_io,
191 origin, base::RefCountedBytes::TakeVector(&png_bytes),
192 gfx::Size(downscaled_image.width(), downscaled_image.height()),
193 gfx::Size(image.width(), image.height())));
194 }
195
196 void NotificationImageReporter::SendReportOnIO(
197 const GURL& origin,
198 scoped_refptr<base::RefCountedMemory> data,
199 const gfx::Size& dimensions,
200 const gfx::Size& original_dimensions) {
201 DCHECK_CURRENTLY_ON(BrowserThread::IO);
202
203 NotificationImageReportRequest report;
204 report.set_notification_origin(origin.spec());
205 report.mutable_image()->set_data(data->front(), data->size());
206 report.mutable_image()->set_mime_type(kDefaultMimeType);
207 report.mutable_image()->mutable_dimensions()->set_width(dimensions.width());
208 report.mutable_image()->mutable_dimensions()->set_height(dimensions.height());
209 if (dimensions != original_dimensions) {
210 report.mutable_image()->mutable_original_dimensions()->set_width(
211 original_dimensions.width());
212 report.mutable_image()->mutable_original_dimensions()->set_height(
213 original_dimensions.height());
214 }
215
216 std::string serialized_report;
217 report.SerializeToString(&serialized_report);
218 report_sender_->Send(
219 GURL(kReportingUploadUrl), "application/octet-stream", serialized_report,
220 base::Bind(&LogReportResult, GURL(kReportingUploadUrl), net::OK),
221 base::Bind(&LogReportResult));
222 // TODO(johnme): Consider logging bandwidth and/or duration to UMA.
223 }
224
225 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698