Support asynchronous continuation of custom scheme handler responses (issue #269).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@278 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2011-08-17 01:55:07 +00:00
parent ad66d3774c
commit 88a46e0b44
14 changed files with 693 additions and 258 deletions

View File

@ -340,6 +340,8 @@
'libcef_dll/cpptoc/request_cpptoc.h',
'libcef_dll/cpptoc/response_cpptoc.cc',
'libcef_dll/cpptoc/response_cpptoc.h',
'libcef_dll/cpptoc/scheme_handler_callback_cpptoc.cc',
'libcef_dll/cpptoc/scheme_handler_callback_cpptoc.h',
'libcef_dll/cpptoc/stream_reader_cpptoc.cc',
'libcef_dll/cpptoc/stream_reader_cpptoc.h',
'libcef_dll/cpptoc/stream_writer_cpptoc.cc',
@ -538,6 +540,8 @@
'libcef_dll/ctocpp/request_ctocpp.h',
'libcef_dll/ctocpp/response_ctocpp.cc',
'libcef_dll/ctocpp/response_ctocpp.h',
'libcef_dll/ctocpp/scheme_handler_callback_ctocpp.cc',
'libcef_dll/ctocpp/scheme_handler_callback_ctocpp.h',
'libcef_dll/ctocpp/stream_reader_ctocpp.cc',
'libcef_dll/ctocpp/stream_reader_ctocpp.h',
'libcef_dll/ctocpp/stream_writer_ctocpp.cc',

View File

@ -2468,9 +2468,35 @@ public:
CefRefPtr<CefRequest> request) =0;
};
///
// Class used to facilitate asynchronous responses to custom scheme handler
// requests. The methods of this class may be called on any thread.
///
/*--cef(source=library)--*/
class CefSchemeHandlerCallback : public virtual CefBase
{
public:
///
// Notify that header information is now available for retrieval.
///
/*--cef()--*/
virtual void HeadersAvailable() =0;
///
// Notify that response data is now available for reading.
///
/*--cef()--*/
virtual void BytesAvailable() =0;
///
// Cancel processing of the request.
///
/*--cef()--*/
virtual void Cancel() =0;
};
///
// Class used to represent a custom scheme handler interface. The methods of
// Class used to implement a custom scheme handler interface. The methods of
// this class will always be called on the IO thread.
///
/*--cef(source=client)--*/
@ -2478,38 +2504,49 @@ class CefSchemeHandler : public virtual CefBase
{
public:
///
// Process the request. All response generation should take place in this
// method. If there is no response set |response_length| to zero or return
// false and ReadResponse() will not be called. If the response length is not
// known set |response_length| to -1 and ReadResponse() will be called until
// it returns false or until the value of |bytes_read| is set to 0. If the
// response length is known set |response_length| to a positive value and
// ReadResponse() will be called until it returns false, the value of
// |bytes_read| is set to 0 or the specified number of bytes have been read.
// Use the |response| object to set the mime type, http status code and
// optional header values for the response and return true. To redirect the
// request to a new URL set |redirectUrl| to the new URL and return true.
// Begin processing the request. To handle the request return true and call
// HeadersAvailable() once the response header information is available
// (HeadersAvailable() can also be called from inside this method if header
// information is available immediately). To redirect the request to a new
// URL set |redirectUrl| to the new URL and return true. To cancel the request
// return false.
///
/*--cef()--*/
virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl,
CefRefPtr<CefResponse> response,
int* response_length) =0;
CefRefPtr<CefSchemeHandlerCallback> callback) =0;
///
// Cancel processing of the request.
// Retrieve response header information. If the response length is not known
// set |response_length| to -1 and ReadResponse() will be called until it
// returns false. If the response length is known set |response_length|
// to a positive value and ReadResponse() will be called until it returns
// false or the specified number of bytes have been read. Use the |response|
// object to set the mime type, http status code and other optional header
// values.
///
/*--cef()--*/
virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length) =0;
///
// Read response data. If data is available immediately copy up to
// |bytes_to_read| bytes into |data_out|, set |bytes_read| to the number of
// bytes copied, and return true. To read the data at a later time set
// |bytes_read| to 0, return true and call BytesAvailable() when the data is
// available. To indicate response completion return false.
///
/*--cef()--*/
virtual bool ReadResponse(void* data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefSchemeHandlerCallback> callback) =0;
///
// Request processing has been canceled.
///
/*--cef()--*/
virtual void Cancel() =0;
///
// Copy up to |bytes_to_read| bytes into |data_out|. If the copy succeeds
// set |bytes_read| to the number of bytes copied and return true. If the
// copy fails return false and ReadResponse() will not be called again.
///
/*--cef()--*/
virtual bool ReadResponse(void* data_out, int bytes_to_read,
int* bytes_read) =0;
};

View File

@ -2270,7 +2270,36 @@ typedef struct _cef_scheme_handler_factory_t
///
// Structure used to represent a custom scheme handler structure. The functions
// Structure used to facilitate asynchronous responses to custom scheme handler
// requests. The functions of this structure may be called on any thread.
///
typedef struct _cef_scheme_handler_callback_t
{
// Base structure.
cef_base_t base;
///
// Notify that header information is now available for retrieval.
///
void (CEF_CALLBACK *headers_available)(
struct _cef_scheme_handler_callback_t* self);
///
// Notify that response data is now available for reading.
///
void (CEF_CALLBACK *bytes_available)(
struct _cef_scheme_handler_callback_t* self);
///
// Cancel processing of the request.
///
void (CEF_CALLBACK *cancel)(struct _cef_scheme_handler_callback_t* self);
} cef_scheme_handler_callback_t;
///
// Structure used to implement a custom scheme handler structure. The functions
// of this structure will always be called on the IO thread.
///
typedef struct _cef_scheme_handler_t
@ -2279,35 +2308,44 @@ typedef struct _cef_scheme_handler_t
cef_base_t base;
///
// Process the request. All response generation should take place in this
// function. If there is no response set |response_length| to zero or return
// false (0) and read_response() will not be called. If the response length is
// not known set |response_length| to -1 and read_response() will be called
// until it returns false (0) or until the value of |bytes_read| is set to 0.
// If the response length is known set |response_length| to a positive value
// and read_response() will be called until it returns false (0), the value of
// |bytes_read| is set to 0 or the specified number of bytes have been read.
// Use the |response| object to set the mime type, http status code and
// optional header values for the response and return true (1). To redirect
// the request to a new URL set |redirectUrl| to the new URL and return true
// (1).
// Begin processing the request. To handle the request return true (1) and
// call headers_available() once the response header information is available
// (headers_available() can also be called from inside this function if header
// information is available immediately). To redirect the request to a new URL
// set |redirectUrl| to the new URL and return true (1). To cancel the request
// return false (0).
///
int (CEF_CALLBACK *process_request)(struct _cef_scheme_handler_t* self,
struct _cef_request_t* request, cef_string_t* redirectUrl,
struct _cef_response_t* response, int* response_length);
struct _cef_scheme_handler_callback_t* callback);
///
// Cancel processing of the request.
// Retrieve response header information. If the response length is not known
// set |response_length| to -1 and read_response() will be called until it
// returns false (0). If the response length is known set |response_length| to
// a positive value and read_response() will be called until it returns false
// (0) or the specified number of bytes have been read. Use the |response|
// object to set the mime type, http status code and other optional header
// values.
///
void (CEF_CALLBACK *cancel)(struct _cef_scheme_handler_t* self);
void (CEF_CALLBACK *get_response_headers)(struct _cef_scheme_handler_t* self,
struct _cef_response_t* response, int64* response_length);
///
// Copy up to |bytes_to_read| bytes into |data_out|. If the copy succeeds set
// |bytes_read| to the number of bytes copied and return true (1). If the copy
// fails return false (0) and read_response() will not be called again.
// Read response data. If data is available immediately copy up to
// |bytes_to_read| bytes into |data_out|, set |bytes_read| to the number of
// bytes copied, and return true (1). To read the data at a later time set
// |bytes_read| to 0, return true (1) and call bytes_available() when the data
// is available. To indicate response completion return false (0).
///
int (CEF_CALLBACK *read_response)(struct _cef_scheme_handler_t* self,
void* data_out, int bytes_to_read, int* bytes_read);
void* data_out, int bytes_to_read, int* bytes_read,
struct _cef_scheme_handler_callback_t* callback);
///
// Request processing has been canceled.
///
void (CEF_CALLBACK *cancel)(struct _cef_scheme_handler_t* self);
} cef_scheme_handler_t;

View File

@ -529,7 +529,7 @@ class RequestProxy : public net::URLRequest::Delegate,
request_->Start();
if (request_->has_upload() &&
if (request_.get() && request_->has_upload() &&
params->load_flags & net::LOAD_ENABLE_UPLOAD_PROGRESS) {
upload_progress_timer_.Start(
base::TimeDelta::FromMilliseconds(

View File

@ -33,6 +33,7 @@
#include <map>
using net::URLRequestStatus;
using WebKit::WebSecurityPolicy;
using WebKit::WebString;
@ -104,30 +105,53 @@ public:
CefUrlRequestJob(net::URLRequest* request,
CefRefPtr<CefSchemeHandler> handler)
: net::URLRequestJob(request),
handler_(handler),
response_length_(0),
url_(request->url()),
remaining_bytes_(0) { }
handler_(handler),
remaining_bytes_(0)
{
}
virtual ~CefUrlRequestJob(){}
virtual ~CefUrlRequestJob()
{
}
virtual void Start() OVERRIDE
{
handler_->Cancel();
// Continue asynchronously.
DCHECK(!async_resolver_);
response_ = new CefResponseImpl();
async_resolver_ = new AsyncResolver(this);
CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod(
async_resolver_.get(), &AsyncResolver::Resolve, url_));
REQUIRE_IOT();
if (!callback_)
callback_ = new Callback(this);
CefRefPtr<CefRequest> req(CefRequest::CreateRequest());
CefString redirectUrl;
// Populate the request data.
static_cast<CefRequestImpl*>(req.get())->Set(request());
// Handler can decide whether to process the request.
bool rv = handler_->ProcessRequest(req, redirectUrl, callback_.get());
if (!rv) {
// Cancel the request.
NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, ERR_ABORTED));
} else if (!redirectUrl.empty()) {
// Treat the request as a redirect.
std::string redirectUrlStr = redirectUrl;
redirect_url_ = GURL(redirectUrlStr);
NotifyHeadersComplete();
}
return;
}
virtual void Kill() OVERRIDE
{
if (async_resolver_) {
async_resolver_->Cancel();
async_resolver_ = NULL;
REQUIRE_IOT();
// Notify the handler that the request has been canceled.
handler_->Cancel();
if (callback_) {
callback_->Detach();
callback_ = NULL;
}
net::URLRequestJob::Kill();
@ -136,52 +160,62 @@ public:
virtual bool ReadRawData(net::IOBuffer* dest, int dest_size, int *bytes_read)
OVERRIDE
{
REQUIRE_IOT();
DCHECK_NE(dest_size, 0);
DCHECK(bytes_read);
// When remaining_bytes_>=0, it means the handler knows the content size
// before hand. We continue to read until
if (remaining_bytes_>=0) {
if (remaining_bytes_ < dest_size)
dest_size = static_cast<int>(remaining_bytes_);
// If we should copy zero bytes because |remaining_bytes_| is zero, short
// circuit here.
if (!dest_size) {
*bytes_read = 0;
return true;
}
// remaining_bytes > 0
bool rv = handler_->ReadResponse(dest->data(), dest_size, bytes_read);
remaining_bytes_ -= *bytes_read;
if (!rv) {
// handler indicated no further data to read
*bytes_read = 0;
}
return true;
} else {
// The handler returns -1 for GetResponseLength, this means the handler
// doesn't know the content size before hand. We do basically the same
// thing, except for checking the return value for handler_->ReadResponse,
// which is an indicator for no further data to be read.
bool rv = handler_->ReadResponse(dest->data(), dest_size, bytes_read);
if (!rv)
// handler indicated no further data to read
*bytes_read = 0;
if (remaining_bytes_ == 0) {
// No more data to read.
*bytes_read = 0;
return true;
} else if (remaining_bytes_ > 0 && remaining_bytes_ < dest_size) {
// The handler knows the content size beforehand.
dest_size = static_cast<int>(remaining_bytes_);
}
// Read response data from the handler.
bool rv = handler_->ReadResponse(dest->data(), dest_size, *bytes_read,
callback_.get());
if (!rv) {
// The handler has indicated completion of the request.
*bytes_read = 0;
return true;
} else if(*bytes_read == 0) {
if (!GetStatus().is_io_pending()) {
// Report our status as IO pending.
SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
callback_->SetDestination(dest, dest_size);
}
return false;
} else if(*bytes_read > dest_size) {
// Normalize the return value.
*bytes_read = dest_size;
}
if(remaining_bytes_ > 0)
remaining_bytes_ -= *bytes_read;
// Continue calling this method.
return true;
}
virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
CefResponseImpl* responseImpl =
static_cast<CefResponseImpl*>(response_.get());
info->headers = responseImpl->GetResponseHeaders();
virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE
{
REQUIRE_IOT();
if (response_.get()) {
CefResponseImpl* responseImpl =
static_cast<CefResponseImpl*>(response_.get());
info->headers = responseImpl->GetResponseHeaders();
}
}
virtual bool IsRedirectResponse(GURL* location, int* http_status_code)
OVERRIDE
{
REQUIRE_IOT();
if (redirect_url_.is_valid()) {
// Redirect to the new URL.
*http_status_code = 303;
@ -193,105 +227,132 @@ public:
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE
{
DCHECK(request_);
// call handler to get mime type
*mime_type = response_->GetMimeType();
REQUIRE_IOT();
if (response_.get())
*mime_type = response_->GetMimeType();
return true;
}
CefRefPtr<CefSchemeHandler> handler_;
CefRefPtr<CefResponse> response_;
int response_length_;
protected:
GURL url_;
GURL redirect_url_;
private:
void DidResolve(const GURL& url)
void SendHeaders()
{
async_resolver_ = NULL;
REQUIRE_IOT();
// We may have been orphaned...
if (!request_)
if (!request())
return;
remaining_bytes_ = response_length_;
if (remaining_bytes_>0)
response_ = new CefResponseImpl();
remaining_bytes_ = 0;
// Get header information from the handler.
handler_->GetResponseHeaders(response_, remaining_bytes_);
if (remaining_bytes_ > 0)
set_expected_content_size(remaining_bytes_);
// Continue processing the request.
NotifyHeadersComplete();
}
int64 remaining_bytes_;
std::string m_response;
class AsyncResolver :
public base::RefCountedThreadSafe<AsyncResolver> {
// Client callback for asynchronous response continuation.
class Callback : public CefSchemeHandlerCallback
{
public:
explicit AsyncResolver(CefUrlRequestJob* owner)
: owner_(owner), owner_loop_(MessageLoop::current()) {
}
Callback(CefUrlRequestJob* job)
: job_(job),
dest_(NULL),
dest_size_() {}
void Resolve(const GURL& url) {
base::AutoLock locked(lock_);
if (!owner_ || !owner_loop_)
return;
//////////////////////////////////////////////////////////////////////////
// safe to perform long operation here
CefRefPtr<CefRequest> req(CefRequest::CreateRequest());
// populate the request data
static_cast<CefRequestImpl*>(req.get())->Set(owner_->request());
owner_->handler_->Cancel();
int response_length = 0;
CefString redirectUrl;
// handler should complete content generation in ProcessRequest
bool res = owner_->handler_->ProcessRequest(req, redirectUrl,
owner_->response_, &response_length);
if (res) {
if (!redirectUrl.empty()) {
// Treat the request as a redirect.
std::string redirectUrlStr = redirectUrl;
owner_->redirect_url_ = GURL(redirectUrlStr);
owner_->response_length_ = 0;
} else {
owner_->response_length_ = response_length;
virtual void HeadersAvailable() OVERRIDE
{
if (CefThread::CurrentlyOn(CefThread::IO)) {
// Currently on IO thread.
if (job_ && !job_->has_response_started()) {
// Send header information.
job_->SendHeaders();
}
}
//////////////////////////////////////////////////////////////////////////
if (owner_loop_) {
owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &AsyncResolver::ReturnResults, url));
} else {
// Execute this method on the IO thread.
CefThread::PostTask(CefThread::IO, FROM_HERE,
NewRunnableMethod(this, &Callback::HeadersAvailable));
}
}
void Cancel() {
owner_->handler_->Cancel();
virtual void BytesAvailable() OVERRIDE
{
if (CefThread::CurrentlyOn(CefThread::IO)) {
// Currently on IO thread.
if (job_ && job_->has_response_started() &&
job_->GetStatus().is_io_pending()) {
// Read the bytes. They should be available but, if not, wait again.
int bytes_read = 0;
if (job_->ReadRawData(dest_, dest_size_, &bytes_read)) {
if (bytes_read > 0) {
// Clear the IO_PENDING status.
job_->SetStatus(URLRequestStatus());
base::AutoLock locked(lock_);
owner_ = NULL;
owner_loop_ = NULL;
// Notify about the available bytes.
job_->NotifyReadComplete(bytes_read);
dest_ = NULL;
dest_size_ = NULL;
}
} else {
// All done.
job_->NotifyDone(URLRequestStatus());
}
}
} else {
// Execute this method on the IO thread.
CefThread::PostTask(CefThread::IO, FROM_HERE,
NewRunnableMethod(this, &Callback::BytesAvailable));
}
}
virtual void Cancel() OVERRIDE
{
if (CefThread::CurrentlyOn(CefThread::IO)) {
// Currently on IO thread.
if (job_)
job_->Kill();
} else {
// Execute this method on the IO thread.
CefThread::PostTask(CefThread::IO, FROM_HERE,
NewRunnableMethod(this, &Callback::Cancel));
}
}
void Detach()
{
REQUIRE_IOT();
job_ = NULL;
}
void SetDestination(net::IOBuffer* dest, int dest_size)
{
dest_ = dest;
dest_size_ = dest_size;
}
static bool ImplementsThreadSafeReferenceCounting() { return true; }
private:
void ReturnResults(const GURL& url) {
if (owner_)
owner_->DidResolve(url);
}
CefUrlRequestJob* job_;
CefUrlRequestJob* owner_;
net::IOBuffer* dest_;
int dest_size_;
base::Lock lock_;
MessageLoop* owner_loop_;
IMPLEMENT_REFCOUNTING(Callback);
};
friend class AsyncResolver;
scoped_refptr<AsyncResolver> async_resolver_;
GURL redirect_url_;
int64 remaining_bytes_;
CefRefPtr<Callback> callback_;
DISALLOW_COPY_AND_ASSIGN(CefUrlRequestJob);
};

View File

@ -0,0 +1,65 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
//
// ---------------------------------------------------------------------------
//
// A portion of this file was generated by the CEF translator tool. When
// making changes by hand only do so within the body of existing function
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
#include "libcef_dll/cpptoc/scheme_handler_callback_cpptoc.h"
// MEMBER FUNCTIONS - Body may be edited by hand.
void CEF_CALLBACK scheme_handler_callback_headers_available(
struct _cef_scheme_handler_callback_t* self)
{
DCHECK(self);
if (!self)
return;
CefSchemeHandlerCallbackCppToC::Get(self)->HeadersAvailable();
}
void CEF_CALLBACK scheme_handler_callback_bytes_available(
struct _cef_scheme_handler_callback_t* self)
{
DCHECK(self);
if (!self)
return;
CefSchemeHandlerCallbackCppToC::Get(self)->BytesAvailable();
}
void CEF_CALLBACK scheme_handler_callback_cancel(
struct _cef_scheme_handler_callback_t* self)
{
DCHECK(self);
if (!self)
return;
CefSchemeHandlerCallbackCppToC::Get(self)->Cancel();
}
// CONSTRUCTOR - Do not edit by hand.
CefSchemeHandlerCallbackCppToC::CefSchemeHandlerCallbackCppToC(
CefSchemeHandlerCallback* cls)
: CefCppToC<CefSchemeHandlerCallbackCppToC, CefSchemeHandlerCallback,
cef_scheme_handler_callback_t>(cls)
{
struct_.struct_.headers_available = scheme_handler_callback_headers_available;
struct_.struct_.bytes_available = scheme_handler_callback_bytes_available;
struct_.struct_.cancel = scheme_handler_callback_cancel;
}
#ifndef NDEBUG
template<> long CefCppToC<CefSchemeHandlerCallbackCppToC,
CefSchemeHandlerCallback, cef_scheme_handler_callback_t>::DebugObjCt = 0;
#endif

View File

@ -0,0 +1,35 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool and should not edited
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
#ifndef _SCHEMEHANDLERCALLBACK_CPPTOC_H
#define _SCHEMEHANDLERCALLBACK_CPPTOC_H
#ifndef BUILDING_CEF_SHARED
#pragma message("Warning: "__FILE__" may be accessed DLL-side only")
#else // BUILDING_CEF_SHARED
#include "include/cef.h"
#include "include/cef_capi.h"
#include "libcef_dll/cpptoc/cpptoc.h"
// Wrap a C++ class with a C structure.
// This class may be instantiated and accessed DLL-side only.
class CefSchemeHandlerCallbackCppToC
: public CefCppToC<CefSchemeHandlerCallbackCppToC, CefSchemeHandlerCallback,
cef_scheme_handler_callback_t>
{
public:
CefSchemeHandlerCallbackCppToC(CefSchemeHandlerCallback* cls);
virtual ~CefSchemeHandlerCallbackCppToC() {}
};
#endif // BUILDING_CEF_SHARED
#endif // _SCHEMEHANDLERCALLBACK_CPPTOC_H

View File

@ -13,26 +13,54 @@
#include "libcef_dll/cpptoc/scheme_handler_cpptoc.h"
#include "libcef_dll/ctocpp/request_ctocpp.h"
#include "libcef_dll/ctocpp/response_ctocpp.h"
#include "libcef_dll/ctocpp/scheme_handler_callback_ctocpp.h"
// MEMBER FUNCTIONS - Body may be edited by hand.
int CEF_CALLBACK scheme_handler_process_request(
struct _cef_scheme_handler_t* self, cef_request_t* request,
cef_string_t* redirectUrl, cef_response_t* response, int* response_length)
cef_string_t* redirectUrl, cef_scheme_handler_callback_t* callback)
{
DCHECK(self);
DCHECK(request);
DCHECK(redirectUrl);
DCHECK(response);
DCHECK(response_length);
if(!self || !request || !redirectUrl || !response || !response_length)
if(!self || !request || !redirectUrl)
return 0;
CefString redirectUrlStr(redirectUrl);
return CefSchemeHandlerCppToC::Get(self)->ProcessRequest(
CefRequestCToCpp::Wrap(request), redirectUrlStr,
CefResponseCToCpp::Wrap(response), response_length);
CefSchemeHandlerCallbackCToCpp::Wrap(callback));
}
void CEF_CALLBACK scheme_handler_get_response_headers(
struct _cef_scheme_handler_t* self, cef_response_t* response,
int64* response_length)
{
DCHECK(self);
DCHECK(response);
DCHECK(response_length);
if (!self || !response || !response_length)
return;
CefSchemeHandlerCppToC::Get(self)->GetResponseHeaders(
CefResponseCToCpp::Wrap(response), *response_length);
}
int CEF_CALLBACK scheme_handler_read_response(
struct _cef_scheme_handler_t* self, void* data_out, int bytes_to_read,
int* bytes_read, cef_scheme_handler_callback_t* callback)
{
DCHECK(self);
DCHECK(data_out);
DCHECK(bytes_read);
if(!self || !data_out || !bytes_read)
return 0;
return CefSchemeHandlerCppToC::Get(self)->ReadResponse(
data_out, bytes_to_read, *bytes_read,
CefSchemeHandlerCallbackCToCpp::Wrap(callback)) ? 1 : 0;
}
void CEF_CALLBACK scheme_handler_cancel(struct _cef_scheme_handler_t* self)
@ -44,22 +72,6 @@ void CEF_CALLBACK scheme_handler_cancel(struct _cef_scheme_handler_t* self)
CefSchemeHandlerCppToC::Get(self)->Cancel();
}
int CEF_CALLBACK scheme_handler_read_response(
struct _cef_scheme_handler_t* self, void* data_out, int bytes_to_read,
int* bytes_read)
{
DCHECK(self);
DCHECK(data_out);
DCHECK(bytes_read);
if(!self || !data_out || !bytes_read)
return 0;
bool rv = CefSchemeHandlerCppToC::Get(self)->ReadResponse(
data_out, bytes_to_read, bytes_read);
return rv?1:0;
}
// CONSTRUCTOR - Do not edit by hand.
@ -68,8 +80,9 @@ CefSchemeHandlerCppToC::CefSchemeHandlerCppToC(CefSchemeHandler* cls)
cls)
{
struct_.struct_.process_request = scheme_handler_process_request;
struct_.struct_.cancel = scheme_handler_cancel;
struct_.struct_.get_response_headers = scheme_handler_get_response_headers;
struct_.struct_.read_response = scheme_handler_read_response;
struct_.struct_.cancel = scheme_handler_cancel;
}
#ifndef NDEBUG

View File

@ -0,0 +1,47 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
//
// ---------------------------------------------------------------------------
//
// A portion of this file was generated by the CEF translator tool. When
// making changes by hand only do so within the body of existing static and
// virtual method implementations. See the translator.README.txt file in the
// tools directory for more information.
//
#include "libcef_dll/ctocpp/scheme_handler_callback_ctocpp.h"
// VIRTUAL METHODS - Body may be edited by hand.
void CefSchemeHandlerCallbackCToCpp::HeadersAvailable()
{
if (CEF_MEMBER_MISSING(struct_, headers_available))
return;
struct_->headers_available(struct_);
}
void CefSchemeHandlerCallbackCToCpp::BytesAvailable()
{
if (CEF_MEMBER_MISSING(struct_, bytes_available))
return;
struct_->bytes_available(struct_);
}
void CefSchemeHandlerCallbackCToCpp::Cancel()
{
if (CEF_MEMBER_MISSING(struct_, cancel))
return;
struct_->cancel(struct_);
}
#ifndef NDEBUG
template<> long CefCToCpp<CefSchemeHandlerCallbackCToCpp,
CefSchemeHandlerCallback, cef_scheme_handler_callback_t>::DebugObjCt = 0;
#endif

View File

@ -0,0 +1,43 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
//
// -------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool and should not edited
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
#ifndef _SCHEMEHANDLERCALLBACK_CTOCPP_H
#define _SCHEMEHANDLERCALLBACK_CTOCPP_H
#ifndef USING_CEF_SHARED
#pragma message("Warning: "__FILE__" may be accessed wrapper-side only")
#else // USING_CEF_SHARED
#include "include/cef.h"
#include "include/cef_capi.h"
#include "libcef_dll/ctocpp/ctocpp.h"
// Wrap a C structure with a C++ class.
// This class may be instantiated and accessed wrapper-side only.
class CefSchemeHandlerCallbackCToCpp
: public CefCToCpp<CefSchemeHandlerCallbackCToCpp, CefSchemeHandlerCallback,
cef_scheme_handler_callback_t>
{
public:
CefSchemeHandlerCallbackCToCpp(cef_scheme_handler_callback_t* str)
: CefCToCpp<CefSchemeHandlerCallbackCToCpp, CefSchemeHandlerCallback,
cef_scheme_handler_callback_t>(str) {}
virtual ~CefSchemeHandlerCallbackCToCpp() {}
// CefSchemeHandlerCallback methods
virtual void HeadersAvailable() OVERRIDE;
virtual void BytesAvailable() OVERRIDE;
virtual void Cancel() OVERRIDE;
};
#endif // USING_CEF_SHARED
#endif // _SCHEMEHANDLERCALLBACK_CTOCPP_H

View File

@ -12,21 +12,41 @@
#include "libcef_dll/cpptoc/request_cpptoc.h"
#include "libcef_dll/cpptoc/response_cpptoc.h"
#include "libcef_dll/cpptoc/scheme_handler_callback_cpptoc.h"
#include "libcef_dll/ctocpp/scheme_handler_ctocpp.h"
// VIRTUAL METHODS - Body may be edited by hand.
bool CefSchemeHandlerCToCpp::ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl, CefRefPtr<CefResponse> response,
int* response_length)
CefString& redirectUrl, CefRefPtr<CefSchemeHandlerCallback> callback)
{
if(CEF_MEMBER_MISSING(struct_, process_request))
return false;
return struct_->process_request(struct_, CefRequestCppToC::Wrap(request),
redirectUrl.GetWritableStruct(), CefResponseCppToC::Wrap(response),
response_length) ? true : false;
redirectUrl.GetWritableStruct(),
CefSchemeHandlerCallbackCppToC::Wrap(callback)) ? true : false;
}
void CefSchemeHandlerCToCpp::GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length)
{
if (CEF_MEMBER_MISSING(struct_, get_response_headers))
return;
struct_->get_response_headers(struct_, CefResponseCppToC::Wrap(response),
&response_length);
}
bool CefSchemeHandlerCToCpp::ReadResponse(void* data_out, int bytes_to_read,
int& bytes_read, CefRefPtr<CefSchemeHandlerCallback> callback)
{
if(CEF_MEMBER_MISSING(struct_, read_response))
return false;
return struct_->read_response(struct_, data_out, bytes_to_read, &bytes_read,
CefSchemeHandlerCallbackCppToC::Wrap(callback)) ? true : false;
}
void CefSchemeHandlerCToCpp::Cancel()
@ -37,16 +57,6 @@ void CefSchemeHandlerCToCpp::Cancel()
struct_->cancel(struct_);
}
bool CefSchemeHandlerCToCpp::ReadResponse(void* data_out, int bytes_to_read,
int* bytes_read)
{
if(CEF_MEMBER_MISSING(struct_, read_response))
return false;
return struct_->read_response(struct_, data_out, bytes_to_read, bytes_read) ?
true : false;
}
#ifndef NDEBUG
template<> long CefCToCpp<CefSchemeHandlerCToCpp, CefSchemeHandler,

View File

@ -34,11 +34,13 @@ public:
// CefSchemeHandler methods
virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl, CefRefPtr<CefResponse> response,
int* response_length) OVERRIDE;
CefString& redirectUrl,
CefRefPtr<CefSchemeHandlerCallback> callback) OVERRIDE;
virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length) OVERRIDE;
virtual bool ReadResponse(void* data_out, int bytes_to_read, int& bytes_read,
CefRefPtr<CefSchemeHandlerCallback> callback) OVERRIDE;
virtual void Cancel() OVERRIDE;
virtual bool ReadResponse(void* data_out, int bytes_to_read,
int* bytes_read) OVERRIDE;
};
#endif // BUILDING_CEF_SHARED

View File

@ -19,21 +19,10 @@ class ClientSchemeHandler : public CefSchemeHandler
public:
ClientSchemeHandler() : offset_(0) {}
// Process the request. All response generation should take place in this
// method. If there is no response set |response_length| to zero or return
// false and ReadResponse() will not be called. If the response length is not
// known set |response_length| to -1 and ReadResponse() will be called until
// it returns false or until the value of |bytes_read| is set to 0. If the
// response length is known set |response_length| to a positive value and
// ReadResponse() will be called until it returns false, the value of
// |bytes_read| is set to 0 or the specified number of bytes have been read.
// Use the |response| object to set the mime type, http status code and
// optional header values for the response and return true. To redirect the
// request to a new URL set |redirectUrl| to the new URL and return true.
virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl,
CefRefPtr<CefResponse> response,
int* response_length)
CefRefPtr<CefSchemeHandlerCallback> callback)
OVERRIDE
{
REQUIRE_IO_THREAD();
@ -65,8 +54,7 @@ public:
handled = true;
// Set the resulting mime type
response->SetMimeType("text/html");
response->SetStatus(200);
mime_type_ = "text/html";
}
else if(strstr(url.c_str(), "client.png") != NULL) {
// Load the response image
@ -77,40 +65,55 @@ public:
data_ = std::string(reinterpret_cast<const char*>(pBytes), dwSize);
handled = true;
// Set the resulting mime type
response->SetMimeType("image/jpg");
response->SetStatus(200);
mime_type_ = "image/jpg";
}
#elif defined(OS_MACOSX)
if(LoadBinaryResource("logo.png", data_)) {
handled = true;
response->SetMimeType("image/png");
response->SetStatus(200);
// Set the resulting mime type
mime_type_ = "image/png";
}
#endif
}
// Set the resulting response length
*response_length = data_.length();
return handled;
if (handled) {
// Indicate the headers are available.
callback->HeadersAvailable();
return true;
}
return false;
}
// Cancel processing of the request.
virtual void Cancel()
virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length) OVERRIDE
{
REQUIRE_IO_THREAD();
ASSERT(!data_.empty());
response->SetMimeType(mime_type_);
response->SetStatus(200);
// Set the resulting response length
response_length = data_.length();
}
virtual void Cancel() OVERRIDE
{
REQUIRE_IO_THREAD();
}
// Copy up to |bytes_to_read| bytes into |data_out|. If the copy succeeds
// set |bytes_read| to the number of bytes copied and return true. If the
// copy fails return false and ReadResponse() will not be called again.
virtual bool ReadResponse(void* data_out, int bytes_to_read,
int* bytes_read)
virtual bool ReadResponse(void* data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefSchemeHandlerCallback> callback)
OVERRIDE
{
REQUIRE_IO_THREAD();
bool has_data = false;
*bytes_read = 0;
bytes_read = 0;
AutoLock lock_scope(this);
@ -121,7 +124,7 @@ public:
memcpy(data_out, data_.c_str() + offset_, transfer_size);
offset_ += transfer_size;
*bytes_read = transfer_size;
bytes_read = transfer_size;
has_data = true;
}
@ -130,6 +133,7 @@ public:
private:
std::string data_;
std::string mime_type_;
size_t offset_;
IMPLEMENT_REFCOUNTING(ClientSchemeHandler);

View File

@ -12,7 +12,8 @@ class TestResults
public:
TestResults()
: status_code(0),
sub_status_code(0)
sub_status_code(0),
delay(0)
{
}
@ -27,6 +28,7 @@ public:
sub_status_code = 0;
sub_allow_origin.clear();
exit_url.clear();
delay = 0;
got_request.reset();
got_read.reset();
got_output.reset();
@ -51,6 +53,9 @@ public:
std::string sub_allow_origin;
std::string exit_url;
// Delay for returning scheme handler results.
int delay;
TrackCallback
got_request,
got_read,
@ -149,21 +154,57 @@ class ClientSchemeHandler : public CefSchemeHandler
{
public:
ClientSchemeHandler(TestResults* tr)
: test_results_(tr), offset_(0), is_sub_(false) {}
: test_results_(tr), offset_(0), is_sub_(false), has_delayed_(false) {}
virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
CefString& redirectUrl,
CefRefPtr<CefResponse> response,
int* response_length)
CefRefPtr<CefSchemeHandlerCallback> callback)
OVERRIDE
{
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
bool handled = false;
std::string url = request->GetURL();
is_sub_ = (!test_results_->sub_url.empty() &&
test_results_->sub_url == url);
if (is_sub_) {
test_results_->got_sub_request.yes();
if (!test_results_->sub_html.empty())
handled = true;
} else {
EXPECT_EQ(url, test_results_->url);
test_results_->got_request.yes();
if (!test_results_->redirect_url.empty()) {
redirectUrl = test_results_->redirect_url;
return true; // don't call Continue() for URL redirects.
} else if (!test_results_->html.empty()) {
handled = true;
}
}
if (handled) {
if (test_results_->delay > 0) {
// Continue after the delay.
CefPostDelayedTask(TID_IO, NewCefRunnableMethod(callback.get(),
&CefSchemeHandlerCallback::HeadersAvailable), test_results_->delay);
} else {
// Continue immediately.
callback->HeadersAvailable();
}
return true;
}
return false;
}
virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length) OVERRIDE
{
if (is_sub_) {
response->SetStatus(test_results_->sub_status_code);
if (!test_results_->sub_allow_origin.empty()) {
@ -177,38 +218,45 @@ public:
if (!test_results_->sub_html.empty()) {
response->SetMimeType("text/html");
*response_length = test_results_->sub_html.size();
return true;
response_length = test_results_->sub_html.size();
}
} else {
EXPECT_EQ(url, test_results_->url);
test_results_->got_request.yes();
response->SetStatus(test_results_->status_code);
if (!test_results_->redirect_url.empty()) {
redirectUrl = test_results_->redirect_url;
return true;
} else if (!test_results_->html.empty()) {
if (!test_results_->html.empty()) {
response->SetMimeType("text/html");
*response_length = test_results_->html.size();
return true;
response_length = test_results_->html.size();
}
}
return false;
}
virtual void Cancel()
virtual void Cancel() OVERRIDE
{
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
}
virtual bool ReadResponse(void* data_out, int bytes_to_read, int* bytes_read)
virtual bool ReadResponse(void* data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefSchemeHandlerCallback> callback)
OVERRIDE
{
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
if (test_results_->delay > 0) {
if (!has_delayed_) {
// Continue after a delay.
CefPostDelayedTask(TID_IO,
NewCefRunnableMethod(this,
&ClientSchemeHandler::ContinueAfterDelay, callback),
test_results_->delay);
bytes_read = 0;
return true;
}
has_delayed_ = false;
}
std::string* data;
if (is_sub_) {
@ -220,7 +268,7 @@ public:
}
bool has_data = false;
*bytes_read = 0;
bytes_read = 0;
AutoLock lock_scope(this);
@ -231,7 +279,7 @@ public:
memcpy(data_out, data->c_str() + offset_, transfer_size);
offset_ += transfer_size;
*bytes_read = transfer_size;
bytes_read = transfer_size;
has_data = true;
}
@ -239,9 +287,16 @@ public:
}
private:
void ContinueAfterDelay(CefRefPtr<CefSchemeHandlerCallback> callback)
{
has_delayed_ = true;
callback->BytesAvailable();
}
TestResults* test_results_;
size_t offset_;
bool is_sub_;
bool has_delayed_;
IMPLEMENT_REFCOUNTING(ClientSchemeHandler);
IMPLEMENT_LOCKING(ClientSchemeHandler);
@ -466,6 +521,27 @@ TEST(SchemeHandlerTest, CustomStandardNormalResponse)
ClearTestSchemes();
}
// Test that a custom standard scheme can return normal results with delayed
// responses.
TEST(SchemeHandlerTest, CustomStandardNormalResponseDelayed)
{
RegisterTestScheme("customstd", "test");
g_TestResults.url = "customstd://test/run.html";
g_TestResults.html =
"<html><head></head><body><h1>Success!</h1></body></html>";
g_TestResults.status_code = 200;
g_TestResults.delay = 100;
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
EXPECT_TRUE(g_TestResults.got_request);
EXPECT_TRUE(g_TestResults.got_read);
EXPECT_TRUE(g_TestResults.got_output);
ClearTestSchemes();
}
// Test that a custom nonstandard scheme can return normal results.
TEST(SchemeHandlerTest, CustomNonStandardNormalResponse)
{