It's been 6 months since I announced CEF2. I appreciate the patience of those of you waiting for CEF2's release and I think it's time to give a progress report on where development stands. To review, there are two steps that I am seeking to complete before making CEF2 development public.
1. Implement something similar to http://codereview.chromium.org/10973/show for Chromium trunk that allows the hosting of Chromium windows in separate applications.
Step 1 is complete. The below screen shots show CEF2 embedding Chromium using a variety of different configurations controlled by command-line arguments. The ability to control Chromium behavior using command-line arguments is important and will be supported by the CEF2 API.
http://www.magpcss.com/pub/cef2_screen1.png
http://www.magpcss.com/pub/cef2_screen2.png
http://www.magpcss.com/pub/cef2_screen3.png
2. Implement a replacement for the existing CEF DLL that interfaces with 1 using an IPC layer and supports the basic CEF functionality. The existing CEF interfaces may be modified slightly to support the concept of multiple tabs.
Step 2 is about halfway complete. The implementation is internally composed of two parts: the CEF2 host layer which exists inside the libcef2 library and the CEF2 browser layer which is compiled as part of Chromium. These two layers communicate with each other via IPC, with CEF2 acting as the server and Chromium acting as the client.
The CEF2 host layer provides two main classes: CefThread (similar to Chromium's ChromeThread class) and CefNotificationService (similar to Chromium's NotificationService class). CefThread manages IO, PROCESS and UI threads. The IO thread is used for IPC communication, the PROCESS thread is used for launching Chromium sub-processes, and the UI thread is used for user interface integration. The UI thread can run separately or can be integrated with the main application message loop as with the current version of CEF. The CefNotificationService, which exists on a per-thread basis, allows consumers of certain event types to register and be notified when an event occurs.
The CEF2 browser layer running in Chromium associates the CEF2 IPC connection, the identifier for which is passed in as a command-line argument, with both the Chromium Profile and the Chromium Browser objects. It is possible for CEF2 to control multiple Chromium processes and browser windows, both embedded and framed, from a single client application. Chromium uses the user profile path to uniquely identify a browser process and so CEF2 follows the same approach. If two separate CEF2 client applications launch Chromium with the same user profile path then those applications will share a single Chromium browser process.
Internal implementation details have by necessity become more complex then with the current version of CEF. Consider, for instance, the CefHandler::HandleBeforeMenu() callback that is currently part of CEF. This method gives the application the option to substitute a custom context menu for the default Chromium context menu. With CEF2 the internal implementation of this callback will work as follows:
1. The CEF2 browser layer is notified by Chromium that the user wishes to show a context menu.
2. The CEF2 browser layer sends an IPC message to the CEF2 host layer which receives the message on the IO thread.
3. The IO thread IPC message handler proxies the message to the UI thread.
4. The UI thread broadcasts the context menu request using the notification service.
5. The CEF2 handler object, which is associated at one end with the client application's handler and at the other end with the source Chromium profile/window, receives the context menu notification and forwards it via the CEF2 API to the client application.
6. The client application performs the desired behavior and returns a response.
7. The CEF2 handler object proxies the response back to the IO thread.
8. The IO thread sends the response as an IPC message to the CEF2 browser layer.
9. The required action, if any, is executed by Chromium.
One driving principal of Chromium design is that all actions should be executed asynchronously if possible. Consequently the CEF2 implementation will make every effort to provide control capabilities in an asynchronous manner. For instance, methods like CefTab::CanGoBack() will be implemented internally by CEF2 as callbacks that are notified when the history state has changed. When the client calls CefTab::CanGoBack() they will be retrieving stored state information instead of initiating additional traffic over the IPC layer.
The API structure for CEF2 is still under consideration, but current development indicates that it will look something like this:
- Call CefInitialize and CefShutdown to initialize and shutdown CEF2, respectively. Calling CefShutdown will reset all state information such that calling CefInitialize again will behave as expected.
- The top-level API class for CEF2 will be CefProfile. CefProfile objects represent a single instance of the Chromium browser process. CefProfile will provide one or more methods for creating a new CefBrowser based on a combination of window placement and command-line information.
- CefBrowser objects will support multiple CefTab objects if the browser is created in standard mode, or a single CefTab object if the browser is created in app mode. CefTab objects can be created and destroyed using methods provided by the parent CefBrowser object.
- CefTab objects will support multiple CefFrame objects.
Instead of continuing the existing CEF approach of providing a single CefHandler class implementation for each CefBrowser it may make more sense to extend the notification service concept for use by the client application. For instance, staying with the CefHandler::HandleBeforeMenu() example, CEF2 might instead implement special handling behavior with an approach like the following:
1. The CEF2 API defines a handler interface:
- Code: Select all
class CefHandlerBeforeMenu
{
public:
virtual RetVal HandleBeforeMenu(CefRefPtr<CefBrowser> browser,
const MenuInfo& menuInfo) =0;
};
2. The CEF2 client implements one or more interfaces in a class:
- Code: Select all
class MyHandlerBeforeMenu : public CefHandlerBeforeMenu
{
virtual RetVal HandleBeforeMenu(CefRefPtr<CefBrowser> browser,
const MenuInfo& menuInfo) {
// Do something here
return RV_HANDLED;
}
};
3. The CEF2 client registers their handler with the CefBrowser object that they want to control.
- Code: Select all
browser->AddHandlerBeforeMenu(new MyHandlerBeforeMenu);
This approach has a number of advantages over the existing single-handler-class approach.
1. The client only needs to implement the handlers they're interested in, and only the interested handlers are actually called.
2. Multiple handlers for each event type can be supported.
3. Handlers can potentially be registered at different granularity levels (register at the CefProfile level to handle events for all CefBrowser objects owned by that profile, register at the CefBrowser level to handle events for all CefTab objects owned by that browser, etc).
Regards,
Marshall