// Copyright (c) 2010 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/ssl/ssl_error_handler.h"
#include "chrome/browser/ssl/ssl_cert_error_handler.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "content/browser/browser_thread.h"
#include "content/browser/renderer_host/resource_dispatcher_host.h"
#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request.h"
SSLErrorHandler::SSLErrorHandler(ResourceDispatcherHost* rdh,
net::URLRequest* request,
ResourceType::Type resource_type)
: manager_(NULL),
request_id_(0, 0),
resource_dispatcher_host_(rdh),
request_url_(request->url()),
resource_type_(resource_type),
request_has_been_notified_(false) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
ResourceDispatcherHostRequestInfo* info =
ResourceDispatcherHost::InfoForRequest(request);
request_id_.child_id = info->child_id();
request_id_.request_id = info->request_id();
if (!ResourceDispatcherHost::RenderViewForRequest(request,
&render_process_host_id_,
&tab_contents_id_))
NOTREACHED();
// This makes sure we don't disappear on the IO thread until we've given an
// answer to the net::URLRequest.
//
// Release in CompleteCancelRequest, CompleteContinueRequest, or
// CompleteTakeNoAction.
AddRef();
}
SSLErrorHandler::~SSLErrorHandler() {}
void SSLErrorHandler::OnDispatchFailed() {
TakeNoAction();
}
void SSLErrorHandler::OnDispatched() {
TakeNoAction();
}
SSLCertErrorHandler* SSLErrorHandler::AsSSLCertErrorHandler() {
return NULL;
}
void SSLErrorHandler::Dispatch() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TabContents* tab_contents = GetTabContents();
if (!tab_contents) {
// We arrived on the UI thread, but the tab we're looking for is no longer
// here.
OnDispatchFailed();
return;
}
// Hand ourselves off to the SSLManager.
manager_ = tab_contents->controller().ssl_manager();
OnDispatched();
}
TabContents* SSLErrorHandler::GetTabContents() {
return tab_util::GetTabContentsByID(render_process_host_id_,
tab_contents_id_);
}
void SSLErrorHandler::CancelRequest() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// We need to complete this task on the IO thread.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(
this, &SSLErrorHandler::CompleteCancelRequest, net::ERR_ABORTED));
}
void SSLErrorHandler::DenyRequest() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// We need to complete this task on the IO thread.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(
this, &SSLErrorHandler::CompleteCancelRequest,
net::ERR_INSECURE_RESPONSE));
}
void SSLErrorHandler::ContinueRequest() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// We need to complete this task on the IO thread.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this, &SSLErrorHandler::CompleteContinueRequest));
}
void SSLErrorHandler::TakeNoAction() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// We need to complete this task on the IO thread.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this, &SSLErrorHandler::CompleteTakeNoAction));
}
void SSLErrorHandler::CompleteCancelRequest(int error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// It is important that we notify the net::URLRequest only once. If we try
// to notify the request twice, it may no longer exist and |this| might have
// already have been deleted.
DCHECK(!request_has_been_notified_);
if (request_has_been_notified_)
return;
net::URLRequest* request =
resource_dispatcher_host_->GetURLRequest(request_id_);
if (request) {
// The request can be NULL if it was cancelled by the renderer (as the
// result of the user navigating to a new page from the location bar).
DVLOG(1) << "CompleteCancelRequest() url: " << request->url().spec();
SSLCertErrorHandler* cert_error = AsSSLCertErrorHandler();
if (cert_error)
request->SimulateSSLError(error, cert_error->ssl_info());
else
request->SimulateError(error);
}
request_has_been_notified_ = true;
// We're done with this object on the IO thread.
Release();
}
void SSLErrorHandler::CompleteContinueRequest() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// It is important that we notify the net::URLRequest only once. If we try to
// notify the request twice, it may no longer exist and |this| might have
// already have been deleted.
DCHECK(!request_has_been_notified_);
if (request_has_been_notified_)
return;
net::URLRequest* request =
resource_dispatcher_host_->GetURLRequest(request_id_);
if (request) {
// The request can be NULL if it was cancelled by the renderer (as the
// result of the user navigating to a new page from the location bar).
DVLOG(1) << "CompleteContinueRequest() url: " << request->url().spec();
request->ContinueDespiteLastError();
}
request_has_been_notified_ = true;
// We're done with this object on the IO thread.
Release();
}
void SSLErrorHandler::CompleteTakeNoAction() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// It is important that we notify the net::URLRequest only once. If we try to
// notify the request twice, it may no longer exist and |this| might have
// already have been deleted.
DCHECK(!request_has_been_notified_);
if (request_has_been_notified_)
return;
request_has_been_notified_ = true;
// We're done with this object on the IO thread.
Release();
}