Opera Core: Tutorials

$Id$

The Core API: PortingInterfaces and WindowCommander

Port Opera in just 200 days!

On the Wiki there is a summary of the estimates time it takes to port Opera to a new platform, based on the ports to Kyocera and PowerTV. The figures are probably guesstimates as much as estimates, but adding them up it comes out to between 110 and 170 man-days for a port of the full browser, not including customer extras.

The core porting API consists of the Porting_Interfaces from the core toward the platform facilities, and the WindowCommander from the core to the platform-specific Opera UI. That is, porting Opera consists to a large part of implementing platform hooks and UI.

This tutorial attempts to explain how to go about doing a port and also tries to explain how things hang together.

First steps

Choose a build environment and a unique all-uppercase platform name (eg, MSWIN, EPOC; I'll use just "P"). Create a directory platforms/p and a cvs module called p. Create platforms/p/features.h by copying one from some other platform and enable and disable the features you want. The complete list is in core/vfunc.txt.

Create a project or Makefile and try to compile. When you run into trouble, add header files as appropriate or insert a platform #ifdef (there are plenty already). If you're nice you will put modifications in a file in your platform directory and include this from eg core/pch.h, and not modify the core code unless you have to.

Iterate until your code compiles and you only have one link error: g_factory is undefined.

g_factory and OpFactory

The abstract class OpFactory defines methods that create instances of platform versions of abstract porting interfaces; the core code uses OpFactory to bootstrap itself into a particular platform setting.

(OpFactory documentation is missing; see Porting_Interfaces/OpFactory.h.)

The first thing you need to do is to define a global variable:

   OpFactory* g_factory = NULL;
Then you subclass OpFactory with a platform-specific implementation class:
   class PlatformOpFactory : public OpFactory { ... };
and define a method on the OpFactory class to create the platform-specific instance:
   OpFactory* OpFactory::Create()
   {
       return new WindowsOpFactory;
   }
Depending on which features you have enabled, you must implement a number of the pure virtual methods of OpFactory.

Once you've implemented the OpFactory you will have a bunch of methods that allocate instances of classes that do not exist--OpBitmap, OpFontManager, OpView, and so on. These must now be implemented by subclassing various Porting_Interface classes with platform-specific representations. Most of the Porting_Interfaces required by OpFactory are straightforward; just read the comments in the header files. OpView and OpWindow are more complicated, however.

OpView, OpWindow, and the OpWindowCommander

An OpView (Porting_Interfaces/OpView.h) represents an area for drawing. The area represented by the OpView may be larger than the area in which the drawing is displayed, so the OpView may have to be able to scroll. OpViews can be nested, but there is always an OpWindow around the outermost OpView.

An OpWindow (Porting_Interfaces/OpWindow.h) represents a user interface window -- a container that the user perceives as being mostly independent of other windows. There are many kinds of windows--desktop windows, document windows, and dialog boxes to mention some--and some have ties to others, without this fact impinging on their window-ness. The OpWindow is itself not an area for drawing: OpViews nested in the window are used for drawing.

For some kinds of windows the OpWindow and its nested views are all that's required, but this it is not sufficient for representing a regular browser window with its history, state, and so on. Browser windows are instead represented by a pair of objects: an OpWindow for the UI aspect and an OpWindowCommander (modules/windowcommander/OpWindowCommander.h) for the additional stuff. The OpWindowCommander presents a rich API that allows the UI to manipulate the browser window state.

Initialization

From your main() you need to create the OpFactory by calling OpFactory::Create and assigning the result to g_factory:

   g_factory = OpFactory::Create();
Normally this is done very early, before any other Core code is used.

The rest of core initialization is currently very messy. Some core initialization is always performed by the platform code, as this allows specialized initialization of some components, allows platform code to insert initialization actions in the middle of the core initialization sequence, or allows core initialization to be performed when the user interface is operational. Your only chance here is to look at an existing platform port to see how it's done.

The message loop

At some point during initialization there is a call to the Run method of the message loop:

   g_messageLoop->Run();
after which Opera starts dispatching messages to installed message handlers. The message loop is an instance of OpMessageLoop, that is, it has a platform implementation created by the OpFactory. The loop does not return to its caller until Opera is ready to terminate, so it must itself process system messages.