You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



How to Port NGLayout
Michael Plitkins
Last updated 8-July-98

So you want to port the new layout engine and it's entrails to another platform? How do you get from zero to 11 in the shortest time possible? The following are my recommendations for accomplishing the task. Everyone has their own way of doing things and if you think you can cut out some steps or do things a bit more efficiently, go for it. In any case, hopefully this will help give you some direction if you're lost in the desert (just make sure you have a computer with a net drop in this mythical desert...).

You need to have the code before you can port it

Go to http://www.mozilla.org/newlayout and grab the New Layout code. It is available both as a tarball with all of the necessary tools, etc. to build it on several different platforms and in a CVS source repository. Look at the platforms to which the layout code has already been ported and find the one that is closest to your target platform. If you're unsure of which one to go with, grab the Unix/Linux code. For each platform, there are directions at Mozilla.org on which files need to be downloaded/pulled. Follow those directions to get the code.
Note that the new layout code uses a bunch of stuff that is part of Mozilla. We call this part of the code the 'dist.' Since Mozilla has been ported to more places than the new layout engine has, it is possible that the dist code has already been ported to the platform that you are targeting and this will make your life easier. Check Mozilla.org for information on ports in progress and their status to see if the code you need has already been ported. If so, you might also be able to adopt the build system in place there. Look a the top level makefile/build script for the list of directories that are considered to be part of the dist. They will be all of those that aren't labeled as part of nglayout. They might even have the obvious name of 'dist.'

Next it needs to compile

Depending on how different your target platform is from the ones for which code exists, you might need to create a new build system. The build system for each of the current platforms is slightly (or vastly) different - under Windows, a combination of nmake and gmake (only for parts of the dist) is used, Unix uses a separate set of makefiles and gmake, and the Mac uses perl to basically run a build script. If you have GNU tools available, you might just get away with the Unix build system. The mozilla/config directory contains a set of files that determine how the build will proceed based on environment variables that are set before the build begins. See the pages at http://www.mozilla.org/newlayout/download.html for information on the various environment settings that need to be made in order to properly build the right bits of code in the dist.

Once a build system seems to work, you need to build things in the right order and build the right executables. Again, look at the existing makefiles to see what order the various pieces need to be built it. Once the various libraries have been created, you will probably get a bunch of compile an link errors if your build is trying to make the various test apps that come with the source code. It would probably be a good idea to get some, or all of the tests to run before attempting to make the actual viewer test app which exercises all of the code in the dist and new layout engine. In each source directory for which there are tests available, there is a tests directory usually containing one or two source files to build a test app to exercise some of the code in the relevant module. It would probably be a good idea to build the tests in the mozilla/xpcom directory as a starter.

Now it links, where to go from here?

If you've got the xpcom test app linking (and maybe it even runs), what do you do next to start making things real? First off, if you don't have a working dist, you need to get that to go before you have a prayer of getting the new layout enging up and running. That's priority one and outside the scope of this document. See Mozilla.org pages for more info. Once the dist code seems healthy, come back here.

There are three parts of the new layout engine that are not cross platform and therefore need to be implemented for each environment to which it is ported. These are parts of mozilla/base, parts of mozilla/gfx and mozilla/widget:

mozilla/base contains a public interface called nsITimer that is used for triggering timeouts. There is also a test directory in base containing a test app to exercise the timer.

mozilla/gfx contains a set of interfaces to enable cross platform rendering of lines, rects, text, images, etc. All of the graphics interfaces are spec'ed in mozilla/gfx/src. The set of interfaces that have XP implementations have corresponding .cpp files in gfx/src. The remaining interfaces are implemented per-platform in subdirectories of gfx/src. For a list of exactly which interfaces need to be implemented, use gfx/src/windows as a guide.

mozilla/widget contains a set of interfaces for cross platform support of user interface widgets (i.e. button, scrollbar, editfield, etc). To date, the implementation of the widgets is completely platform dependent so there is no code shared among the various implementations. The scope of widgets and there functionality is essentially that required to support HTML form elements. The interfaces are defined in widget/public and there are per-platform subdirectories in widget/src.

In addition, there are some non XP parts of the viewer test app that will need to be ported, and they live in mozilla/webshell/tests/viewer. This is mostly code to attach menus to an nsIWidget and run the main message dispatch loop. Basically, the equivalent of main() needs to be written that brings up a window (there is XP code for doing this using the widget library), attach some menus for debugging, handle the main app message loop and respond to menu events. See webshell/tests/viewer/winmain.cpp for an example of what needs to be done.

In order to have a fully working layout engine, each of these non XP pieces of code needs to be fully implemented, but to get things limping along, a minimal subset can be implemented (along with additional simple test apps) to bring up simple HTML pages.

Crawl, Walk, Run

Get the timer test app to work since in order for the viewer to ever link there needs to be some implementation of nsITimer. It's also another good test of the xpcom code.

At this point, were you to write a version of the non XP code in mozilla/webshell/tests/viewer you would be able to link the viewer but it would fail to run until there are factories for the graphics and widget objects.

It is the order in which these factories and the objects that they build for you that is the key to getting something up and running quickly. I would first recommend writing the factories themselves (clone the Windows version) and then write stub implementations of each of the interfaces that need to be implemented in gfx and widget. Many of the interface methods will require that return codes be provided and/or that output parameters be supplied in "positive" ways. This means that you should attempt to have methods that return something to the caller return values that "make sense" and allow the caller to continue working rather than just failing. If you do this, then the methods can be implemented for real one-by-one in the right order and slowly parts of the viewer and other test apps will begin to work. We really need to have boiler plate implementations of the various interfaces, but we don't at this time.

To begin implementing things for real, start with the widgets. Four (really two) basic widgets need to work in order for the viewer to function. These are, according to CID: NS_WINDOW_CID, NS_CHILD_CID, NS_HORZSCROLLBAR_CID and NS_VERTSCROLLBAR_CID.

NS_WINDOW_CID is a top level window with chrome and represents the base window class.

NS_CHILD_CID is a window like NS_WINDOW_CID, but without chrome.

NS_HORZSCROLLBAR_CID and NS_VERTSCROLLBAR_CID represent the two basic varieties of scrollbars.

Care must be taken in implementing the widget classes in that the new layout engine uses xpcom aggregation with the widgets to bind them to layout engine "views." What this means is that the layout engine, given a pointer to a widget, will call the widget's QueryInterface() method asking for an object of type nsIView which has been associated with the widget when it was instantiated. It is important that the widget hold a reference to the object aggregated in and if the IID passed in to QueryInterface() is not understood, then it is passed into the aggregated object's QueryInterface() and the result of that is returned. If this mechanism doesn't work then event handling in the viewer will be broken (hint, hint). This code should really be factored out so that it doesn't need to be implemented per-platform.

As far as event handling goes, see nsGUIEvent.h for a list of the events that need to be supported. The most critical are those that are related to window geometry, painting, scrolling and mouse handling. Once you have some basic widgets working, you can check to see that you are passing the proper events at the proper time to the application by putting a break point in the global event handler in mozilla/views/src/nsView.cpp. It's the C callback at the top of the file. It cares about resize and paint events which are the most fundamental to driving the viewer.

Once you start getting paint events, it would be a good time to have the mozilla/gfx code working so that you can render what the layout code has processed. An intermediate step to getting the viewer to render (which really exercises all of the rendering classes to the maximum extent) is to get the Scribble test app in widget/tests/scribble to work to some degree. It will try to create and use more widgets than the viewer will, but at the very least, a working Scribble app will come up and allow you to click in the right hand side of the window, move the mouse and draw lines. If you want to implement more of the widgets that it tries to instantiate, additional functionality will be revealed... There is also a windows-only test app in mozilla/gfx/tests/btest that you might want to port if you're having difficulty with the rendering context, blender, or image classes.

The non XP implementations of the mozilla/gfx classes that must work and what in them must work follow in prioritized order:

nsIDeviceContext: everything here is fairly critical. You can lie about the DPI queries and return identity values.

nsIFontMetrics: you can initially support a single font for all font requests, but all metric queries must return sensible values or text layout won't work right. If you lie about the queries, then the results of rendering text may result in large gaps or overlaps between groups of words when rendered.

nsIRenderingContext: all methods that return something from the rendering context must work including those for handling drawing surfaces. They are used for double buffering. Clipping APIs must work, along with PushState() and PopState(). The critical objects that need to be pushed/popped are: clip regions, color, font. If any of these don't get pushed/popped properly, some garbage wil appear in the output, most obvious is incorrect clip region state management. The more output methods that work, the more you will see in your HTML documents.

nsIRegion: all methods except for the rect enumerator need to work.

nsIImage: at the very least, this needs to properly instantiate itself so that HTML pages with images don't fail. Beyond that, it needs to be able to draw itself. Eventually, the Optimize() method should work to improve performace of image rendering if possible.

nsIBlender: currently, not critical to functionality of viewer.

When you get the viewer to begin loading and rendering pages, finish fleshing out the rendering classes and then continue with the widgets. Once the widgets work, you will be able to display pages with HTML form elements.

At good points in the crawl, walk, run growth process submit your code to the appropriate Mozilla.org module owners so that it gets incorporated back into the source distribution and other like minded people can aid the whole development effort.