// Copyright (c) 2012 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/net/resource_prefetch_predictor_observer.h"

#include <string>

#include "base/metrics/histogram.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/resource_request_info.h"
#include "googleurl/src/gurl.h"
#include "net/url_request/url_request.h"

using content::BrowserThread;
using predictors::ResourcePrefetchPredictor;

namespace {

// Enum for measuring statistics pertaining to observed request, responses and
// redirects.
enum RequestStats {
  REQUEST_STATS_TOTAL_RESPONSES = 0,
  REQUEST_STATS_TOTAL_PROCESSED_RESPONSES = 1,
  REQUEST_STATS_NO_RESOURCE_REQUEST_INFO = 2,
  REQUEST_STATS_NO_RENDER_VIEW_ID_FROM_REQUEST_INFO = 3,
  REQUEST_STATS_MAX = 4,
};

// Specific to main frame requests.
enum MainFrameRequestStats {
  MAIN_FRAME_REQUEST_STATS_TOTAL_REQUESTS = 0,
  MAIN_FRAME_REQUEST_STATS_PROCESSED_REQUESTS = 1,
  MAIN_FRAME_REQUEST_STATS_TOTAL_REDIRECTS = 2,
  MAIN_FRAME_REQUEST_STATS_PROCESSED_REDIRECTS = 3,
  MAIN_FRAME_REQUEST_STATS_TOTAL_RESPONSES = 4,
  MAIN_FRAME_REQUEST_STATS_PROCESSED_RESPONSES = 5,
  MAIN_FRAME_REQUEST_STATS_MAX = 6,
};

void ReportRequestStats(RequestStats stat) {
  UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.RequestStats",
                            stat,
                            REQUEST_STATS_MAX);
}

void ReportMainFrameRequestStats(MainFrameRequestStats stat) {
  UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.MainFrameRequestStats",
                            stat,
                            MAIN_FRAME_REQUEST_STATS_MAX);
}

bool SummarizeResponse(net::URLRequest* request,
                       ResourcePrefetchPredictor::URLRequestSummary* summary) {
  const content::ResourceRequestInfo* info =
      content::ResourceRequestInfo::ForRequest(request);
  if (!info) {
    ReportRequestStats(REQUEST_STATS_NO_RESOURCE_REQUEST_INFO);
    return false;
  }

  int render_process_id, render_view_id;
  if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id)) {
    ReportRequestStats(REQUEST_STATS_NO_RENDER_VIEW_ID_FROM_REQUEST_INFO);
    return false;
  }

  summary->navigation_id.render_process_id = render_process_id;
  summary->navigation_id.render_view_id = render_view_id;
  summary->navigation_id.main_frame_url = request->first_party_for_cookies();
  summary->navigation_id.creation_time = request->creation_time();
  summary->resource_url = request->original_url();
  summary->resource_type = info->GetResourceType();
  request->GetMimeType(&summary->mime_type);
  summary->was_cached = request->was_cached();

  // Use the mime_type to determine the resource type for subresources since
  // types such as PREFETCH, SUB_RESOURCE, etc are not useful.
  if (summary->resource_type != ResourceType::MAIN_FRAME) {
    summary->resource_type =
        ResourcePrefetchPredictor::GetResourceTypeFromMimeType(
            summary->mime_type,
            summary->resource_type);
  }
  return true;
}

}  // namespace

namespace chrome_browser_net {

ResourcePrefetchPredictorObserver::ResourcePrefetchPredictorObserver(
    ResourcePrefetchPredictor* predictor)
    : predictor_(predictor->AsWeakPtr()) {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

ResourcePrefetchPredictorObserver::~ResourcePrefetchPredictorObserver() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
        BrowserThread::CurrentlyOn(BrowserThread::IO));
}

void ResourcePrefetchPredictorObserver::OnRequestStarted(
    net::URLRequest* request,
    ResourceType::Type resource_type,
    int child_id,
    int route_id) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  if (resource_type == ResourceType::MAIN_FRAME)
    ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_TOTAL_REQUESTS);

  if (!ResourcePrefetchPredictor::ShouldRecordRequest(request, resource_type))
    return;

  ResourcePrefetchPredictor::URLRequestSummary summary;
  summary.navigation_id.render_process_id = child_id;
  summary.navigation_id.render_view_id = route_id;
  summary.navigation_id.main_frame_url = request->first_party_for_cookies();
  summary.resource_url = request->original_url();
  summary.resource_type = resource_type;

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&ResourcePrefetchPredictor::RecordURLRequest,
                 predictor_,
                 summary));

  if (resource_type == ResourceType::MAIN_FRAME)
    ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_PROCESSED_REQUESTS);
}

void ResourcePrefetchPredictorObserver::OnRequestRedirected(
    const GURL& redirect_url,
    net::URLRequest* request) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  const content::ResourceRequestInfo* request_info =
      content::ResourceRequestInfo::ForRequest(request);
  if (request_info &&
      request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
    ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_TOTAL_REDIRECTS);
  }

  if (!ResourcePrefetchPredictor::ShouldRecordRedirect(request))
    return;

  ResourcePrefetchPredictor::URLRequestSummary summary;
  if (!SummarizeResponse(request, &summary))
    return;

  summary.redirect_url = redirect_url;

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&ResourcePrefetchPredictor::RecordUrlRedirect,
                 predictor_,
                 summary));

  if (request_info &&
      request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
    ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_PROCESSED_REDIRECTS);
  }
}

void ResourcePrefetchPredictorObserver::OnResponseStarted(
    net::URLRequest* request) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  ReportRequestStats(REQUEST_STATS_TOTAL_RESPONSES);

  const content::ResourceRequestInfo* request_info =
      content::ResourceRequestInfo::ForRequest(request);
  if (request_info &&
      request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
    ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_TOTAL_RESPONSES);
  }

  if (!ResourcePrefetchPredictor::ShouldRecordResponse(request))
    return;
  ResourcePrefetchPredictor::URLRequestSummary summary;
  if (!SummarizeResponse(request, &summary))
    return;

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&ResourcePrefetchPredictor::RecordUrlResponse,
                 predictor_,
                 summary));

  ReportRequestStats(REQUEST_STATS_TOTAL_PROCESSED_RESPONSES);
  if (request_info &&
      request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
    ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_PROCESSED_RESPONSES);
  }
}

}  // namespace chrome_browser_net