| Index: chrome/browser/safe_browsing/notification_image_reporter.cc
|
| diff --git a/chrome/browser/safe_browsing/notification_image_reporter.cc b/chrome/browser/safe_browsing/notification_image_reporter.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e2a82f7c61c6bf00a8f1112dc1059de43992bba5
|
| --- /dev/null
|
| +++ b/chrome/browser/safe_browsing/notification_image_reporter.cc
|
| @@ -0,0 +1,178 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/safe_browsing/notification_image_reporter.h"
|
| +
|
| +#include <cmath>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/memory/ref_counted_memory.h"
|
| +#include "base/rand_util.h"
|
| +#include "base/threading/sequenced_worker_pool.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/safe_browsing/safe_browsing_service.h"
|
| +#include "chrome/common/safe_browsing/csd.pb.h"
|
| +#include "components/safe_browsing_db/database_manager.h"
|
| +#include "components/safe_browsing_db/safe_browsing_prefs.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "net/url_request/report_sender.h"
|
| +#include "skia/ext/image_operations.h"
|
| +#include "third_party/skia/include/core/SkBitmap.h"
|
| +#include "ui/gfx/codec/png_codec.h"
|
| +#include "ui/gfx/geometry/size.h"
|
| +#include "url/gurl.h"
|
| +
|
| +using content::BrowserThread;
|
| +
|
| +namespace safe_browsing {
|
| +
|
| +namespace {
|
| +
|
| +const size_t kMaxReportsPerDay = 5;
|
| +
|
| +const char kReportingUploadUrl[] =
|
| + "https://safebrowsing.googleusercontent.com/safebrowsing/clientreport/"
|
| + "chrome-notification-image"; // TODO(johnme): Confirm URL.
|
| +
|
| +} // namespace
|
| +
|
| +NotificationImageReporter::NotificationImageReporter(
|
| + net::URLRequestContext* request_context)
|
| + : NotificationImageReporter(base::MakeUnique<net::ReportSender>(
|
| + request_context,
|
| + net::ReportSender::CookiesPreference::DO_NOT_SEND_COOKIES)) {}
|
| +
|
| +NotificationImageReporter::NotificationImageReporter(
|
| + std::unique_ptr<net::ReportSender> report_sender)
|
| + : report_sender_(std::move(report_sender)) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +}
|
| +
|
| +NotificationImageReporter::~NotificationImageReporter() {
|
| + // Thread on which this is destroyed may vary.
|
| +}
|
| +
|
| +void NotificationImageReporter::ReportNotificationImageOnUI(
|
| + Profile* profile,
|
| + const GURL& origin,
|
| + const SkBitmap& image) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + DCHECK_EQ(origin, origin.GetOrigin());
|
| +
|
| + // Skip reporting unless SBER2 Scout is enabled.
|
| + switch (GetExtendedReportingLevel(*profile->GetPrefs())) {
|
| + case SBER_LEVEL_OFF:
|
| + case SBER_LEVEL_LEGACY:
|
| + return;
|
| + case SBER_LEVEL_SCOUT:
|
| + break;
|
| + }
|
| +
|
| + // Sample a Finch-controlled fraction only.
|
| + double report_chance = GetReportChance();
|
| + if (base::RandDouble() >= report_chance)
|
| + return;
|
| +
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&NotificationImageReporter::ReportNotificationImageOnIO, this,
|
| + make_scoped_refptr(g_browser_process->safe_browsing_service()),
|
| + origin, image));
|
| +}
|
| +
|
| +double NotificationImageReporter::GetReportChance() {
|
| + return 0.2; // TODO(johnme): Get this from Finch.
|
| +}
|
| +
|
| +void NotificationImageReporter::ReportNotificationImageOnIO(
|
| + scoped_refptr<SafeBrowsingService> safe_browsing_service,
|
| + const GURL& origin,
|
| + const SkBitmap& image) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +
|
| + // Skip whitelisted origins to cut down on report volume.
|
| + if (!safe_browsing_service || !safe_browsing_service->database_manager() ||
|
| + safe_browsing_service->database_manager()->MatchCsdWhitelistUrl(origin)) {
|
| + return;
|
| + }
|
| +
|
| + // Avoid exceeding kMaxReportsPerDay.
|
| + base::Time a_day_ago = base::Time::Now() - base::TimeDelta::FromDays(1);
|
| + while (!report_times_.empty() &&
|
| + report_times_.front() < /* older than */ a_day_ago) {
|
| + report_times_.pop();
|
| + }
|
| + if (report_times_.size() >= kMaxReportsPerDay)
|
| + return;
|
| + report_times_.push(base::Time::Now());
|
| +
|
| + BrowserThread::GetBlockingPool()->PostWorkerTask(
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &NotificationImageReporter::DownscaleNotificationImageOnBlockingPool,
|
| + this, origin, image));
|
| +}
|
| +
|
| +void NotificationImageReporter::DownscaleNotificationImageOnBlockingPool(
|
| + const GURL& origin,
|
| + const SkBitmap& image) {
|
| + DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| +
|
| + // Downscale to fit within 512x512. TODO(johnme): Get this from Finch.
|
| + const double MAX_SIZE = 512;
|
| + double scale = std::min(MAX_SIZE / image.width(), MAX_SIZE / image.height());
|
| + SkBitmap downscaled_image =
|
| + scale >= 1.0 ? image // already small enough
|
| + : skia::ImageOperations::Resize(
|
| + image, skia::ImageOperations::RESIZE_GOOD,
|
| + std::lround(scale * image.width()),
|
| + std::lround(scale * image.height()));
|
| +
|
| + // Encode as PNG.
|
| + std::vector<unsigned char> png_bytes;
|
| + if (!gfx::PNGCodec::EncodeBGRASkBitmap(downscaled_image, false, &png_bytes)) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&NotificationImageReporter::SendReportOnIO, this, origin,
|
| + base::RefCountedBytes::TakeVector(&png_bytes),
|
| + gfx::Size(downscaled_image.width(), downscaled_image.height()),
|
| + gfx::Size(image.width(), image.height())));
|
| +}
|
| +
|
| +void NotificationImageReporter::SendReportOnIO(
|
| + const GURL& origin,
|
| + scoped_refptr<base::RefCountedMemory> png_data,
|
| + const gfx::Size& dimensions,
|
| + const gfx::Size& original_dimensions) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| +
|
| + NotificationImageReportRequest report;
|
| + report.set_notification_origin(origin.spec());
|
| + report.mutable_image()->set_png_data(png_data->front(), png_data->size());
|
| + report.mutable_image()->mutable_dimensions()->set_width(dimensions.width());
|
| + report.mutable_image()->mutable_dimensions()->set_height(dimensions.height());
|
| + if (dimensions != original_dimensions) {
|
| + report.mutable_image()->mutable_original_dimensions()->set_width(
|
| + original_dimensions.width());
|
| + report.mutable_image()->mutable_original_dimensions()->set_height(
|
| + original_dimensions.height());
|
| + }
|
| +
|
| + std::string serialized_report;
|
| + report.SerializeToString(&serialized_report);
|
| + report_sender_->Send(GURL(kReportingUploadUrl), "application/octet-stream",
|
| + serialized_report, base::Closure(),
|
| + base::Callback<void(const GURL&, int)>());
|
| + // TODO(johnme): Consider logging bandwidth and/or duration to UMA.
|
| +}
|
| +
|
| +} // namespace safe_browsing
|
|
|