// 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 "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h"

#include <utility>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h"
#include "content/public/browser/browser_thread.h"
#include "url/gurl.h"

namespace subresource_filter {

constexpr base::TimeDelta
    SubresourceFilterSafeBrowsingClientRequest::kCheckURLTimeout;

SubresourceFilterSafeBrowsingClientRequest::
    SubresourceFilterSafeBrowsingClientRequest(
        size_t request_id,
        scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
            database_manager,
        scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
        SubresourceFilterSafeBrowsingClient* client)
    : request_id_(request_id),
      database_manager_(std::move(database_manager)),
      client_(client) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  timer_.SetTaskRunner(std::move(io_task_runner));
}

SubresourceFilterSafeBrowsingClientRequest::
    ~SubresourceFilterSafeBrowsingClientRequest() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  if (!request_completed_)
    database_manager_->CancelCheck(this);
  timer_.Stop();
}

void SubresourceFilterSafeBrowsingClientRequest::Start(const GURL& url) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  start_time_ = base::TimeTicks::Now();

  // Just return SAFE if the database is not supported.
  bool synchronous_finish =
      !database_manager_->IsSupported() ||
      database_manager_->CheckUrlForSubresourceFilter(url, this);
  if (synchronous_finish) {
    request_completed_ = true;
    SendCheckResultToClient(false /* served_from_network */,
                            safe_browsing::SB_THREAT_TYPE_SAFE,
                            safe_browsing::ThreatMetadata());
    return;
  }
  timer_.Start(
      FROM_HERE, kCheckURLTimeout,
      base::BindOnce(
          &SubresourceFilterSafeBrowsingClientRequest::OnCheckUrlTimeout,
          base::Unretained(this)));
}

void SubresourceFilterSafeBrowsingClientRequest::OnCheckBrowseUrlResult(
    const GURL& url,
    safe_browsing::SBThreatType threat_type,
    const safe_browsing::ThreatMetadata& metadata) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  request_completed_ = true;
  SendCheckResultToClient(true /* served_from_network */, threat_type,
                          metadata);
}

void SubresourceFilterSafeBrowsingClientRequest::OnCheckUrlTimeout() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  SendCheckResultToClient(true /* served_from_network */,
                          safe_browsing::SB_THREAT_TYPE_SAFE,
                          safe_browsing::ThreatMetadata());
}

void SubresourceFilterSafeBrowsingClientRequest::SendCheckResultToClient(
    bool served_from_network,
    safe_browsing::SBThreatType threat_type,
    const safe_browsing::ThreatMetadata& metadata) {
  SubresourceFilterSafeBrowsingClient::CheckResult result;
  result.request_id = request_id_;
  result.threat_type = threat_type;
  result.threat_metadata = metadata;
  result.check_time = base::TimeTicks::Now() - start_time_;

  // This memeber is separate from |request_completed_|, in that it just
  // indicates that this request is done processing (due to completion or
  // timeout).
  result.finished = true;

  UMA_HISTOGRAM_TIMES("SubresourceFilter.SafeBrowsing.CheckTime",
                      result.check_time);
  // Will delete |this|.
  client_->OnCheckBrowseUrlResult(this, result);
}

}  // namespace subresource_filter
