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

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: Updated/confirmed URL for reporting 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";
41 const char kDefaultMimeType[] = "image/png";
42
43 void LogReportResult(const GURL& url, int net_error) {
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()) {
Jialiu Lin 2017/01/19 18:54:47 Finch checking does not need to be in the IO threa
harkness 2017/01/19 20:09:09 Unfortunately, IsReportingEnabled is virtual, and
Jialiu Lin 2017/01/19 23:10:40 Acknowledged.
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");
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 switch (GetExtendedReportingLevel(*profile->GetPrefs())) {
Jialiu Lin 2017/01/19 18:54:47 nit: since you only care about scout, it is more c
harkness 2017/01/19 20:09:08 I refactored the switch into an if which is much c
Jialiu Lin 2017/01/19 23:10:40 Acknowledged.
138 case SBER_LEVEL_OFF:
139 case SBER_LEVEL_LEGACY:
140 BrowserThread::PostTask(
141 BrowserThread::IO, FROM_HERE,
142 base::Bind(&NotificationImageReporter::SkippedReporting,
143 weak_this_on_io));
144 return;
145 case SBER_LEVEL_SCOUT:
146 break;
147 }
148
149 BrowserThread::GetBlockingPool()->PostWorkerTask(
150 FROM_HERE,
151 base::Bind(
152 &NotificationImageReporter::DownscaleNotificationImageOnBlockingPool,
153 weak_this_on_io, origin, image));
154 }
155
156 // static
157 void NotificationImageReporter::DownscaleNotificationImageOnBlockingPool(
158 const base::WeakPtr<NotificationImageReporter>& weak_this_on_io,
159 const GURL& origin,
160 const SkBitmap& image) {
161 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
Jialiu Lin 2017/01/19 18:54:47 Is this still on UI thread? Maybe add DCHECK_CURRE
harkness 2017/01/19 20:09:09 No, the blocking pool threads are distinct from th
Jialiu Lin 2017/01/19 23:10:40 Acknowledged.
162
163 // Downscale to fit within 512x512. TODO(johnme): Get this from Finch.
164 const double MAX_SIZE = 512;
165 SkBitmap downscaled_image = image;
166 if ((image.width() > MAX_SIZE || image.height() > MAX_SIZE) &&
167 image.width() > 0 && image.height() > 0) {
168 double scale =
169 std::min(MAX_SIZE / image.width(), MAX_SIZE / image.height());
170 downscaled_image =
171 skia::ImageOperations::Resize(image, skia::ImageOperations::RESIZE_GOOD,
172 std::lround(scale * image.width()),
173 std::lround(scale * image.height()));
174 }
175
176 // Encode as PNG.
177 std::vector<unsigned char> png_bytes;
Jialiu Lin 2017/01/19 18:54:46 how about DCHECK(gfx::PNGCodec::EncodeBGRASkBitmap
harkness 2017/01/19 20:09:09 That would break things if DCHECKs weren't enabled
Jialiu Lin 2017/01/19 23:10:39 Acknowledged.
178 if (!gfx::PNGCodec::EncodeBGRASkBitmap(downscaled_image, false, &png_bytes)) {
179 NOTREACHED();
180 return;
181 }
182
183 BrowserThread::PostTask(
184 BrowserThread::IO, FROM_HERE,
185 base::Bind(&NotificationImageReporter::SendReportOnIO, weak_this_on_io,
186 origin, base::RefCountedBytes::TakeVector(&png_bytes),
187 gfx::Size(downscaled_image.width(), downscaled_image.height()),
188 gfx::Size(image.width(), image.height())));
189 }
190
191 void NotificationImageReporter::SendReportOnIO(
192 const GURL& origin,
193 scoped_refptr<base::RefCountedMemory> data,
194 const gfx::Size& dimensions,
195 const gfx::Size& original_dimensions) {
196 DCHECK_CURRENTLY_ON(BrowserThread::IO);
197
198 NotificationImageReportRequest report;
199 report.set_notification_origin(origin.spec());
200 report.mutable_image()->set_data(data->front(), data->size());
201 report.mutable_image()->set_mime_type(kDefaultMimeType);
202 report.mutable_image()->mutable_dimensions()->set_width(dimensions.width());
203 report.mutable_image()->mutable_dimensions()->set_height(dimensions.height());
204 if (dimensions != original_dimensions) {
205 report.mutable_image()->mutable_original_dimensions()->set_width(
206 original_dimensions.width());
207 report.mutable_image()->mutable_original_dimensions()->set_height(
208 original_dimensions.height());
209 }
210
211 std::string serialized_report;
212 report.SerializeToString(&serialized_report);
213 report_sender_->Send(
214 GURL(kReportingUploadUrl), "application/octet-stream", serialized_report,
215 base::Bind(&LogReportResult, GURL(kReportingUploadUrl), net::OK),
216 base::Bind(&LogReportResult));
217 // TODO(johnme): Consider logging bandwidth and/or duration to UMA.
218 }
219
220 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698