I've written some code to provide custom scheme handler to CEF. The concept is similar to Internet Explorer's Asynchronous Pluggable Protocol, but with CEF it's much easier. The implementation is quite straightforward thanks to the existing CEF classes.
In a nutshell, the scheme handler interface is as below:
- Code: Select all
#ifndef _CEF_SCHEME_H
#define _CEF_SCHEME_H
#include <string>
#include "include/cef.h"
#include "include/cef_ptr.h"
#include "include/cef_types.h"
class CefSchemeHandler;
class CefSchemeHandlerFactory;
// Register a custom scheme handler using the CefSchemeHandler interface
// Custom scheme in the format of scheme_name://host_name/ will be handled through
// the specified handler interface.
// All URLs beginning with scheme_name://host_name/ will go through this handler
/*--cef()--*/
bool CefRegisterScheme( const std::wstring& scheme_name,
const std::wstring& host_name,
CefRefPtr<CefSchemeHandlerFactory> factory);
// Class that creates CefSchemeHandler instances.
/*--cef(source=client)--*/
class CefSchemeHandlerFactory : public CefBase
{
public:
// Return a new scheme handler instance to handle the request.
/*--cef()--*/
virtual CefRefPtr<CefSchemeHandler> Create() =0;
};
// Class used to represent a custom scheme handler interface.
/*--cef(source=client)--*/
class CefSchemeHandler : public CefBase
{
public:
// Process the request. All response generation should take place in this
// method.
/*--cef()--*/
virtual bool ProcessRequest(CefRefPtr<CefRequest> request) =0;
// Cancel processing of the request.
/*--cef()--*/
virtual void Cancel() =0;
// Return the mime type for the request.
/*--cef()--*/
virtual std::wstring GetMimeType() =0;
// Return the response length for the request. If there is no response return
// 0 and ReadResponse() will not be called. If the response length is not
// known return -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 return 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.
/*--cef()--*/
virtual size_t GetResponseLength() =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;
};
#endif
In the attachment package, source files that implements this mechanism are provided. Files have been put outside the CEF source tree. The above interface is defined in libcef/scheme/cef_scheme.h , and the exported function CefRegisterScheme is implemented in libcef/scheme/scheme_impl.cc . It talks to URLRequestFilter to let chromium use custom scheme handlers.
libcef_dll/scheme/cef_scheme_capi.h contains the translated C interface using tools/translator_scheme.bat . And the rest files:
libcef_dll/cpptoc/scheme_handler_cpptoc.cc
libcef_dll/cpptoc/scheme_handler_cpptoc.h
libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc
libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h
libcef_dll/ctocpp/scheme_handler_ctocpp.cc
libcef_dll/ctocpp/scheme_handler_ctocpp.h
libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc
libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h
libcef_dll/scheme/cef_scheme_capi.cc
libcef_dll/wrapper/scheme/scheme_wrapper.cc
are all binding/wrapper implementations.
tests/cefclient/sample_scheme.h contains a sample scheme handler, and some small modifications in cefclient.cpp . To test out this handler, click here to download a 8MB zip file (compiled with chromium R22728). It contains a patched version of chromium to make XMLHttpRequest work with custom schemes (see notes below). The test client looks like this:
![Image](http://www.imediaman.com/offsite/cef_scheme_demo.jpg)
* Note:
It is discovered that although an XMLHttpRequest may generate a POST request that goes through our custom scheme handler like a form post, the upload data (XML data posted) will be lost. The cause is that WebKit's XMLHttpRequest checks the URL and see if it's HTTP or HTTPS protocol before setting post data. This is done inside XMLHttpRequest::send of third_party/WebKit/WebCore/xml/XMLHttpRequest.cpp. The method calls KURL::protocolInHTTPFamily to determine whether post data should be created for the request.
I'm not really sure where to patch. I picked KURLGooglePrivate::initProtocolInHTTPFamily in third_party/WebKit/WebCore/platform/KURLGoogle.cpp , and changed:
- Code: Select all
if (m_parsed.scheme.len == 4)
m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 4, "http");
else if (m_parsed.scheme.len == 5)
m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 5, "https");
else
m_protocolInHTTPFamily = false;
to:
- Code: Select all
switch (m_parsed.scheme.len) {
case 3:
m_protocolInHTTPFamily = !lowerCaseEqualsASCII(scheme, scheme + 3, "ftp"); break;
case 4:
m_protocolInHTTPFamily = !lowerCaseEqualsASCII(scheme, scheme + 4, "file"); break;
case 5:
m_protocolInHTTPFamily = !lowerCaseEqualsASCII(scheme, scheme + 5, "about"); break;
case 10:
m_protocolInHTTPFamily = !lowerCaseEqualsASCII(scheme, scheme + 10, "view-cache"); break;
default:
m_protocolInHTTPFamily = true; break;
}
This solved the problem. Hopefully the chromium team will know about this.
Any comments or new feature requests are welcomed.