root/chrome/browser/renderer_host/safe_browsing_resource_throttle.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. is_subresource_
  2. WillStartRequest
  3. WillRedirectRequest
  4. GetNameForLogging
  5. OnCheckBrowseUrlResult
  6. StartDisplayingBlockingPage
  7. Cancel
  8. OnBlockingPageComplete
  9. CheckUrl
  10. OnCheckUrlTimeout
  11. ResumeRequest

// 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/renderer_host/safe_browsing_resource_throttle.h"

#include "base/logging.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prerender/prerender_contents.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_controller.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_request.h"

// Maximum time in milliseconds to wait for the safe browsing service to
// verify a URL. After this amount of time the outstanding check will be
// aborted, and the URL will be treated as if it were safe.
static const int kCheckUrlTimeoutMs = 5000;

// TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
//               unit test coverage.

SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle(
    const net::URLRequest* request,
    bool is_subresource,
    SafeBrowsingService* safe_browsing)
    : state_(STATE_NONE),
      defer_state_(DEFERRED_NONE),
      threat_type_(SB_THREAT_TYPE_SAFE),
      database_manager_(safe_browsing->database_manager()),
      ui_manager_(safe_browsing->ui_manager()),
      request_(request),
      is_subresource_(is_subresource) {
}

SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() {
  if (state_ == STATE_CHECKING_URL)
    database_manager_->CancelCheck(this);
}

void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer) {
  // We need to check the new URL before starting the request.
  if (CheckUrl(request_->url()))
    return;

  // If the URL couldn't be verified synchronously, defer starting the
  // request until the check has completed.
  defer_state_ = DEFERRED_START;
  *defer = true;
}

void SafeBrowsingResourceThrottle::WillRedirectRequest(const GURL& new_url,
                                                       bool* defer) {
  CHECK(state_ == STATE_NONE);
  CHECK(defer_state_ == DEFERRED_NONE);

  // Save the redirect urls for possible malware detail reporting later.
  redirect_urls_.push_back(new_url);

  // We need to check the new URL before following the redirect.
  if (CheckUrl(new_url))
    return;

  // If the URL couldn't be verified synchronously, defer following the
  // redirect until the SafeBrowsing check is complete. Store the redirect
  // context so we can pass it on to other handlers once we have completed
  // our check.
  defer_state_ = DEFERRED_REDIRECT;
  *defer = true;
}

const char* SafeBrowsingResourceThrottle::GetNameForLogging() const {
  return "SafeBrowsingResourceThrottle";
}

// SafeBrowsingService::Client implementation, called on the IO thread once
// the URL has been classified.
void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult(
    const GURL& url, SBThreatType threat_type) {
  CHECK(state_ == STATE_CHECKING_URL);
  CHECK(defer_state_ != DEFERRED_NONE);
  CHECK(url == url_being_checked_) << "Was expecting: " << url_being_checked_
                                   << " but got: " << url;

  timer_.Stop();  // Cancel the timeout timer.
  threat_type_ = threat_type;
  state_ = STATE_NONE;

  if (threat_type == SB_THREAT_TYPE_SAFE) {
    // Log how much time the safe browsing check cost us.
    ui_manager_->LogPauseDelay(base::TimeTicks::Now() - url_check_start_time_);

    // Continue the request.
    ResumeRequest();
    return;
  }

  if (request_->load_flags() & net::LOAD_PREFETCH) {
    // Don't prefetch resources that fail safe browsing, disallow
    // them.
    controller()->Cancel();
    return;
  }

  const content::ResourceRequestInfo* info =
      content::ResourceRequestInfo::ForRequest(request_);

  SafeBrowsingUIManager::UnsafeResource resource;
  resource.url = url;
  resource.original_url = request_->original_url();
  resource.redirect_urls = redirect_urls_;
  resource.is_subresource = is_subresource_;
  resource.threat_type = threat_type;
  resource.callback = base::Bind(
      &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr());
  resource.render_process_host_id = info->GetChildID();
  resource.render_view_id =  info->GetRouteID();

  state_ = STATE_DISPLAYING_BLOCKING_PAGE;

  content::BrowserThread::PostTask(
      content::BrowserThread::UI,
      FROM_HERE,
      base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage,
                 AsWeakPtr(), ui_manager_, resource));
}

void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage(
    const base::WeakPtr<SafeBrowsingResourceThrottle>& throttle,
    scoped_refptr<SafeBrowsingUIManager> ui_manager,
    const SafeBrowsingUIManager::UnsafeResource& resource) {
  bool should_show_blocking_page = true;

  content::RenderViewHost* rvh = content::RenderViewHost::FromID(
      resource.render_process_host_id, resource.render_view_id);
  if (rvh) {
    content::WebContents* web_contents =
        content::WebContents::FromRenderViewHost(rvh);
    prerender::PrerenderContents* prerender_contents =
        prerender::PrerenderContents::FromWebContents(web_contents);
    if (prerender_contents) {
      prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING);
      should_show_blocking_page = false;
    }

    if (should_show_blocking_page)  {
      ui_manager->DisplayBlockingPage(resource);
      return;
    }
  }

  // Tab is gone or it's being prerendered.
  content::BrowserThread::PostTask(
      content::BrowserThread::IO,
      FROM_HERE,
      base::Bind(&SafeBrowsingResourceThrottle::Cancel, throttle));
}

void SafeBrowsingResourceThrottle::Cancel() {
  controller()->Cancel();
}

// SafeBrowsingService::UrlCheckCallback implementation, called on the IO
// thread when the user has decided to proceed with the current request, or
// go back.
void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) {
  CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE);
  state_ = STATE_NONE;

  if (proceed) {
    threat_type_ = SB_THREAT_TYPE_SAFE;
    ResumeRequest();
  } else {
    controller()->Cancel();
  }
}

bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) {
  CHECK(state_ == STATE_NONE);
  bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this);
  if (succeeded_synchronously) {
    threat_type_ = SB_THREAT_TYPE_SAFE;
    ui_manager_->LogPauseDelay(base::TimeDelta());  // No delay.
    return true;
  }

  state_ = STATE_CHECKING_URL;
  url_being_checked_ = url;

  // Record the start time of the check.
  url_check_start_time_ = base::TimeTicks::Now();

  // Start a timer to abort the check if it takes too long.
  timer_.Start(FROM_HERE,
               base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs),
               this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout);

  return false;
}

void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() {
  CHECK(state_ == STATE_CHECKING_URL);
  CHECK(defer_state_ != DEFERRED_NONE);

  database_manager_->CancelCheck(this);
  OnCheckBrowseUrlResult(url_being_checked_, SB_THREAT_TYPE_SAFE);
}

void SafeBrowsingResourceThrottle::ResumeRequest() {
  CHECK(state_ == STATE_NONE);
  CHECK(defer_state_ != DEFERRED_NONE);

  defer_state_ = DEFERRED_NONE;
  controller()->Resume();
}

/* [<][>][^][v][top][bottom][index][help] */