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

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: Refactored scout check 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/logging.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "base/metrics/field_trial.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 "content/public/browser/browser_thread.h"
25 #include "net/base/net_errors.h"
26 #include "net/url_request/report_sender.h"
27 #include "skia/ext/image_operations.h"
28 #include "third_party/skia/include/core/SkBitmap.h"
29 #include "ui/gfx/codec/png_codec.h"
30 #include "ui/gfx/geometry/size.h"
31 #include "url/gurl.h"
32
33 using content::BrowserThread;
34
35 namespace safe_browsing {
36
37 namespace {
38
39 const size_t kMaxReportsPerDay = 5;
40 const char kImageReporting[] = "NotificationImageReporting";
Nathan Parker 2017/01/20 00:25:43 nit: Add "FieldTrial" (or "Feature" if you go with
harkness 2017/01/20 16:58:06 Done.
41 const char kDefaultMimeType[] = "image/png";
42
43 void LogReportResult(const GURL& url, int net_error) {
Ilya Sherman 2017/01/19 22:53:40 nit: Looks like the |url| is not used, so it could
Nathan Parker 2017/01/20 00:25:43 Looks like that's part of the ReportSender interfa
harkness 2017/01/20 16:58:06 Done.
44 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.NotificationImageReporter.NetError",
45 net_error);
46 }
47
48 } // namespace
49
50 const char NotificationImageReporter::kReportingUploadUrl[] =
51 "https://safebrowsing.googleusercontent.com/safebrowsing/clientreport/"
52 "notification-image";
53
54 NotificationImageReporter::NotificationImageReporter(
55 net::URLRequestContext* request_context)
56 : NotificationImageReporter(base::MakeUnique<net::ReportSender>(
57 request_context,
58 net::ReportSender::CookiesPreference::DO_NOT_SEND_COOKIES)) {}
59
60 NotificationImageReporter::NotificationImageReporter(
61 std::unique_ptr<net::ReportSender> report_sender)
62 : report_sender_(std::move(report_sender)), weak_factory_on_io_(this) {
63 DCHECK_CURRENTLY_ON(BrowserThread::IO);
64
65 // Create an experiment to control whether this instance sends image reports.
66 // The group will have 0% enabled and will only be turned on using Finch.
67 scoped_refptr<base::FieldTrial> trial(
68 base::FieldTrialList::FactoryGetFieldTrial(
69 kImageReporting, 100, "disabled", 2018, 1, 1,
70 base::FieldTrial::SESSION_RANDOMIZED, nullptr));
71 trial->AppendGroup("enabled", 0);
72 }
73
74 NotificationImageReporter::~NotificationImageReporter() {
75 DCHECK_CURRENTLY_ON(BrowserThread::IO);
76 }
77
78 void NotificationImageReporter::ReportNotificationImageOnIO(
79 Profile* profile,
80 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
81 const GURL& origin,
82 const SkBitmap& image) {
83 DCHECK_CURRENTLY_ON(BrowserThread::IO);
84 DCHECK(profile);
85 DCHECK_EQ(origin, origin.GetOrigin());
86 DCHECK(origin.is_valid());
87
88 // Skip whitelisted origins to cut down on report volume.
89 if (!database_manager || database_manager->MatchCsdWhitelistUrl(origin)) {
90 SkippedReporting();
91 return;
92 }
93
94 // Sample a Finch-controlled fraction only.
95 if (!IsReportingEnabled()) {
96 SkippedReporting();
97 return;
98 }
99
100 // Avoid exceeding kMaxReportsPerDay.
101 base::Time a_day_ago = base::Time::Now() - base::TimeDelta::FromDays(1);
102 while (!report_times_.empty() &&
103 report_times_.front() < /* older than */ a_day_ago) {
104 report_times_.pop();
105 }
106 if (report_times_.size() >= kMaxReportsPerDay) {
107 SkippedReporting();
108 return;
109 }
110 // n.b. we write to report_times_ here even if we'll later end up skipping
111 // reporting because GetExtendedReportingLevel was not SBER_LEVEL_SCOUT. That
112 // saves us two thread hops, with the downside that we may underreport
113 // notifications on the first day that a user opts in to SBER_LEVEL_SCOUT.
114 report_times_.push(base::Time::Now());
115
116 BrowserThread::PostTask(
117 BrowserThread::UI, FROM_HERE,
118 base::Bind(&NotificationImageReporter::ReportNotificationImageOnUI,
119 weak_factory_on_io_.GetWeakPtr(), profile, origin, image));
120 }
121
122 bool NotificationImageReporter::IsReportingEnabled() const {
123 return (base::FieldTrialList::FindFullName(kImageReporting) == "enabled");
Nathan Parker 2017/01/20 00:25:43 This is a bit inflexible since you can never chang
Nathan Parker 2017/01/20 01:37:28 Actually, this isn't sufficient to select a random
harkness 2017/01/20 16:58:06 Done.
124 }
125
126 void NotificationImageReporter::SkippedReporting() {}
127
128 // static
129 void NotificationImageReporter::ReportNotificationImageOnUI(
130 const base::WeakPtr<NotificationImageReporter>& weak_this_on_io,
131 Profile* profile,
132 const GURL& origin,
133 const SkBitmap& image) {
134 DCHECK_CURRENTLY_ON(BrowserThread::UI);
135
136 // Skip reporting unless SBER2 Scout is enabled.
137 if (GetExtendedReportingLevel(*profile->GetPrefs()) != SBER_LEVEL_SCOUT) {
138 BrowserThread::PostTask(
139 BrowserThread::IO, FROM_HERE,
140 base::Bind(&NotificationImageReporter::SkippedReporting,
141 weak_this_on_io));
142 return;
143 }
144
145 BrowserThread::GetBlockingPool()->PostWorkerTask(
146 FROM_HERE,
147 base::Bind(
148 &NotificationImageReporter::DownscaleNotificationImageOnBlockingPool,
149 weak_this_on_io, origin, image));
150 }
151
152 // static
153 void NotificationImageReporter::DownscaleNotificationImageOnBlockingPool(
154 const base::WeakPtr<NotificationImageReporter>& weak_this_on_io,
155 const GURL& origin,
156 const SkBitmap& image) {
157 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
158
159 // Downscale to fit within 512x512. TODO(johnme): Get this from Finch.
160 const double MAX_SIZE = 512;
161 SkBitmap downscaled_image = image;
162 if ((image.width() > MAX_SIZE || image.height() > MAX_SIZE) &&
163 image.width() > 0 && image.height() > 0) {
164 double scale =
165 std::min(MAX_SIZE / image.width(), MAX_SIZE / image.height());
166 downscaled_image =
167 skia::ImageOperations::Resize(image, skia::ImageOperations::RESIZE_GOOD,
168 std::lround(scale * image.width()),
169 std::lround(scale * image.height()));
170 }
171
172 // Encode as PNG.
173 std::vector<unsigned char> png_bytes;
174 if (!gfx::PNGCodec::EncodeBGRASkBitmap(downscaled_image, false, &png_bytes)) {
175 NOTREACHED();
176 return;
177 }
178
179 BrowserThread::PostTask(
180 BrowserThread::IO, FROM_HERE,
181 base::Bind(&NotificationImageReporter::SendReportOnIO, weak_this_on_io,
182 origin, base::RefCountedBytes::TakeVector(&png_bytes),
183 gfx::Size(downscaled_image.width(), downscaled_image.height()),
184 gfx::Size(image.width(), image.height())));
185 }
186
187 void NotificationImageReporter::SendReportOnIO(
188 const GURL& origin,
189 scoped_refptr<base::RefCountedMemory> data,
190 const gfx::Size& dimensions,
191 const gfx::Size& original_dimensions) {
192 DCHECK_CURRENTLY_ON(BrowserThread::IO);
193
194 NotificationImageReportRequest report;
195 report.set_notification_origin(origin.spec());
196 report.mutable_image()->set_data(data->front(), data->size());
197 report.mutable_image()->set_mime_type(kDefaultMimeType);
198 report.mutable_image()->mutable_dimensions()->set_width(dimensions.width());
199 report.mutable_image()->mutable_dimensions()->set_height(dimensions.height());
200 if (dimensions != original_dimensions) {
201 report.mutable_image()->mutable_original_dimensions()->set_width(
202 original_dimensions.width());
203 report.mutable_image()->mutable_original_dimensions()->set_height(
204 original_dimensions.height());
205 }
206
207 std::string serialized_report;
208 report.SerializeToString(&serialized_report);
209 report_sender_->Send(
210 GURL(kReportingUploadUrl), "application/octet-stream", serialized_report,
211 base::Bind(&LogReportResult, GURL(kReportingUploadUrl), net::OK),
212 base::Bind(&LogReportResult));
213 // TODO(johnme): Consider logging bandwidth and/or duration to UMA.
214 }
215
216 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698