// 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 "android_webview/browser/aw_login_delegate.h"
#include "android_webview/browser/aw_browser_context.h"
#include "base/android/jni_android.h"
#include "base/logging.h"
#include "base/supports_user_data.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h"
#include "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "net/base/auth.h"
#include "net/url_request/url_request.h"
using namespace base::android;
using content::BrowserThread;
using content::RenderFrameHost;
using content::ResourceDispatcherHost;
using content::ResourceRequestInfo;
using content::WebContents;
using data_reduction_proxy::DataReductionProxyAuthRequestHandler;
using data_reduction_proxy::DataReductionProxySettings;
namespace {
const char* kAuthAttemptsKey = "android_webview_auth_attempts";
class UrlRequestAuthAttemptsData : public base::SupportsUserData::Data {
public:
UrlRequestAuthAttemptsData() : auth_attempts_(0) { }
int auth_attempts_;
};
} // namespace
namespace android_webview {
AwLoginDelegate::AwLoginDelegate(net::AuthChallengeInfo* auth_info,
net::URLRequest* request)
: auth_info_(auth_info),
request_(request),
render_process_id_(0),
render_frame_id_(0) {
ResourceRequestInfo::GetRenderFrameForRequest(
request, &render_process_id_, &render_frame_id_);
UrlRequestAuthAttemptsData* count =
static_cast<UrlRequestAuthAttemptsData*>(
request->GetUserData(kAuthAttemptsKey));
if (count == NULL) {
count = new UrlRequestAuthAttemptsData();
request->SetUserData(kAuthAttemptsKey, count);
}
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&AwLoginDelegate::HandleHttpAuthRequestOnUIThread,
this, (count->auth_attempts_ == 0)));
count->auth_attempts_++;
}
AwLoginDelegate::~AwLoginDelegate() {
// The Auth handler holds a ref count back on |this| object, so it should be
// impossible to reach here while this object still owns an auth handler.
DCHECK(aw_http_auth_handler_ == NULL);
}
void AwLoginDelegate::Proceed(const base::string16& user,
const base::string16& password) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AwLoginDelegate::ProceedOnIOThread,
this, user, password));
}
void AwLoginDelegate::Cancel() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AwLoginDelegate::CancelOnIOThread, this));
}
void AwLoginDelegate::HandleHttpAuthRequestOnUIThread(
bool first_auth_attempt) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
aw_http_auth_handler_.reset(AwHttpAuthHandlerBase::Create(
this, auth_info_.get(), first_auth_attempt));
RenderFrameHost* render_frame_host = RenderFrameHost::FromID(
render_process_id_, render_frame_id_);
WebContents* web_contents = WebContents::FromRenderFrameHost(
render_frame_host);
AwBrowserContext* browser_context =
AwBrowserContext::FromWebContents(web_contents);
DataReductionProxySettings* drp_settings =
browser_context->GetDataReductionProxySettings();
if (drp_settings && drp_settings->IsDataReductionProxyEnabled()) {
// The data reduction proxy auth handler should only be reset on the first
// auth attempt, because it maintains internal state to cancel if there have
// been too many attempts.
if (!drp_auth_handler_.get()) {
drp_auth_handler_.reset(new DataReductionProxyAuthRequestHandler(
drp_settings));
}
DCHECK(drp_auth_handler_.get());
base::string16 user, password;
DataReductionProxyAuthRequestHandler::TryHandleResult drp_result =
drp_auth_handler_->TryHandleAuthentication(
auth_info_.get(), &user, &password);
if (drp_result ==
DataReductionProxyAuthRequestHandler::TRY_HANDLE_RESULT_PROCEED) {
Proceed(user, password);
return;
}
if (drp_result ==
DataReductionProxyAuthRequestHandler::TRY_HANDLE_RESULT_CANCEL) {
// Give up. Disable the proxy and retry.
drp_settings->SetDataReductionProxyEnabled(false);
Proceed(user, password);
return;
}
// Fall through if |drp_result| is IGNORE
}
if (!aw_http_auth_handler_->HandleOnUIThread(web_contents)) {
Cancel();
return;
}
}
void AwLoginDelegate::CancelOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (request_) {
request_->CancelAuth();
ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request_);
request_ = NULL;
}
DeleteAuthHandlerSoon();
}
void AwLoginDelegate::ProceedOnIOThread(const base::string16& user,
const base::string16& password) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (request_) {
request_->SetAuth(net::AuthCredentials(user, password));
ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request_);
request_ = NULL;
}
DeleteAuthHandlerSoon();
}
void AwLoginDelegate::OnRequestCancelled() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
request_ = NULL;
DeleteAuthHandlerSoon();
}
void AwLoginDelegate::DeleteAuthHandlerSoon() {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&AwLoginDelegate::DeleteAuthHandlerSoon, this));
return;
}
aw_http_auth_handler_.reset();
}
} // namespace android_webview