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

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: 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
42 void LogReportResult(const GURL& url, int net_error) {
43 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.NotificationImageReporter.NetError",
44 net_error);
45 }
46
47 } // namespace
48
49 const char NotificationImageReporter::kReportingUploadUrl[] =
50 "https://safebrowsing.googleusercontent.com/safebrowsing/clientreport/"
51 "chrome-notification-image"; // TODO(johnme): Confirm URL.
52
53 NotificationImageReporter::NotificationImageReporter(
54 net::URLRequestContext* request_context)
55 : NotificationImageReporter(base::MakeUnique<net::ReportSender>(
56 request_context,
57 net::ReportSender::CookiesPreference::DO_NOT_SEND_COOKIES)) {}
58
59 NotificationImageReporter::NotificationImageReporter(
60 std::unique_ptr<net::ReportSender> report_sender)
61 : report_sender_(std::move(report_sender)), weak_factory_on_io_(this) {
62 DCHECK_CURRENTLY_ON(BrowserThread::IO);
63
64 // Create an experiment to control whether this instance sends image reports.
65 scoped_refptr<base::FieldTrial> trial(
66 base::FieldTrialList::FactoryGetFieldTrial(
67 kImageReporting, 100, "disabled", 2018, 1, 1,
68 base::FieldTrial::SESSION_RANDOMIZED, nullptr));
69 trial->AppendGroup("enabled", 20);
johnme 2017/01/19 08:58:53 The plan is to land this with a zero percent chanc
harkness 2017/01/19 16:20:55 Updated to 0 for the default.
70 }
71
72 NotificationImageReporter::~NotificationImageReporter() {
73 DCHECK_CURRENTLY_ON(BrowserThread::IO);
74 }
75
76 void NotificationImageReporter::ReportNotificationImageOnIO(
77 Profile* profile,
78 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
79 const GURL& origin,
80 const SkBitmap& image) {
81 DCHECK_CURRENTLY_ON(BrowserThread::IO);
82 DCHECK(profile);
83 DCHECK_EQ(origin, origin.GetOrigin());
84 DCHECK(origin.is_valid());
85
86 // Skip whitelisted origins to cut down on report volume.
87 if (!database_manager || database_manager->MatchCsdWhitelistUrl(origin)) {
88 SkippedReporting();
89 return;
90 }
91
92 // Sample a Finch-controlled fraction only.
93 if (!IsReportingEnabled()) {
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 bool NotificationImageReporter::IsReportingEnabled() const {
121 return (base::FieldTrialList::FindFullName(kImageReporting) == "enabled");
122 }
123
124 void NotificationImageReporter::SkippedReporting() {}
125
126 // static
127 void NotificationImageReporter::ReportNotificationImageOnUI(
128 const base::WeakPtr<NotificationImageReporter>& weak_this_on_io,
129 Profile* profile,
130 const GURL& origin,
131 const SkBitmap& image) {
132 DCHECK_CURRENTLY_ON(BrowserThread::UI);
133
134 // Skip reporting unless SBER2 Scout is enabled.
135 switch (GetExtendedReportingLevel(*profile->GetPrefs())) {
136 case SBER_LEVEL_OFF:
137 case SBER_LEVEL_LEGACY:
138 BrowserThread::PostTask(
139 BrowserThread::IO, FROM_HERE,
140 base::Bind(&NotificationImageReporter::SkippedReporting,
141 weak_this_on_io));
142 return;
143 case SBER_LEVEL_SCOUT:
144 break;
145 }
146
147 BrowserThread::GetBlockingPool()->PostWorkerTask(
148 FROM_HERE,
149 base::Bind(
150 &NotificationImageReporter::DownscaleNotificationImageOnBlockingPool,
151 weak_this_on_io, origin, image));
152 }
153
154 // static
155 void NotificationImageReporter::DownscaleNotificationImageOnBlockingPool(
156 const base::WeakPtr<NotificationImageReporter>& weak_this_on_io,
157 const GURL& origin,
158 const SkBitmap& image) {
159 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
160
161 // Downscale to fit within 512x512. TODO(johnme): Get this from Finch.
162 const double MAX_SIZE = 512;
163 double scale = std::min(MAX_SIZE / image.width(), MAX_SIZE / image.height());
Nathan Parker 2017/01/19 01:22:09 Check for div by zero
johnme 2017/01/19 08:58:53 Technically this is fine, as infinity is greater t
harkness 2017/01/19 16:20:55 I modified the code as John suggested, but also th
164 SkBitmap downscaled_image =
165 scale >= 1.0 ? image // already small enough
166 : skia::ImageOperations::Resize(
167 image, skia::ImageOperations::RESIZE_GOOD,
168 std::lround(scale * image.width()),
169 std::lround(scale * image.height()));
170
171 // Encode as PNG.
172 std::vector<unsigned char> png_bytes;
173 if (!gfx::PNGCodec::EncodeBGRASkBitmap(downscaled_image, false, &png_bytes)) {
174 NOTREACHED();
175 return;
176 }
177
178 BrowserThread::PostTask(
179 BrowserThread::IO, FROM_HERE,
180 base::Bind(&NotificationImageReporter::SendReportOnIO, weak_this_on_io,
181 origin, base::RefCountedBytes::TakeVector(&png_bytes),
182 gfx::Size(downscaled_image.width(), downscaled_image.height()),
183 gfx::Size(image.width(), image.height())));
184 }
185
186 void NotificationImageReporter::SendReportOnIO(
187 const GURL& origin,
188 scoped_refptr<base::RefCountedMemory> png_data,
189 const gfx::Size& dimensions,
190 const gfx::Size& original_dimensions) {
191 DCHECK_CURRENTLY_ON(BrowserThread::IO);
192
193 NotificationImageReportRequest report;
194 report.set_notification_origin(origin.spec());
195 report.mutable_image()->set_png_data(png_data->front(), png_data->size());
196 report.mutable_image()->mutable_dimensions()->set_width(dimensions.width());
197 report.mutable_image()->mutable_dimensions()->set_height(dimensions.height());
198 if (dimensions != original_dimensions) {
199 report.mutable_image()->mutable_original_dimensions()->set_width(
200 original_dimensions.width());
201 report.mutable_image()->mutable_original_dimensions()->set_height(
202 original_dimensions.height());
203 }
204
205 std::string serialized_report;
206 report.SerializeToString(&serialized_report);
207 report_sender_->Send(
208 GURL(kReportingUploadUrl), "application/octet-stream", serialized_report,
209 base::Bind(&LogReportResult, GURL(kReportingUploadUrl), net::OK),
210 base::Bind(&LogReportResult));
211 // TODO(johnme): Consider logging bandwidth and/or duration to UMA.
212 }
213
214 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698