webclient implementation guide
This section discusses the interface and implementation object hierarchies. Refer to the following sections:
There is one BrowserControlFactory instance per Webclient application. This instance is used to create BrowserControl instances. There is one BrowserControl instance per browser window. Each BrowserControl instance is composed of several constituent interfaces. Instances of these interfaces can be obtained by invoking queryInterface() on the BrowserControl instance, passing in the appropriate string of the form "webclient.BrowserControlCanvas", "webclient.CurrentPage", etc.
The custom app adds the BrowserControlCanvas instance to its window hierarchy. The custom app can be notified of webclient events by implementing a WebclientEventListener subinterface and adding that implementation via the EventRegistration interface. The custom-app-defined listener will then receive WebclientEvent instances of the appropriate type at the appropriate time.
Similarly, the custom app can be notified of mouse events by implementing a MouseListener interface and adding that implementation via EventRegistration. The custom app-defined Listener will receive java.awt.event.MouseEvent instances at the appropriate time. These instances can be queried to get a WCMouseEvent object from which individual properties of the event can be obtained. The custom app can display the browser's bookmarks in a JTree using the Bookmarks interface.
The following UML diagram shows the primary private classes that comprise the Java wrapper for the native implementation. Bookmarks and other secondary classes are dealt with separately. Note that classes BrowserControlImpl, ImplObject, and WrapperFactory are in the org.mozilla.webclient Java package, while all the other classes are in the org.mozilla.webclient.wrapper_native package. In general, when one implementation class interacts with another, the interaction occurs through the public interface. That is, Webclient classes don't have access to the implementation details of other Webclient classes.
WrapperFactoryImpl and BrowserControlImpl
BrowserControlImpl implements the BrowserControl interface. BrowserControlImpl uses a concrete subclass of WrapperFactory, WrapperFactoryImpl, to create instances of implementations of Webclient interfaces. The concrete WrapperFactory subclass is provided by the wrapper_native package. See classes_spec/org/mozilla/webclient/WrapperFactory.java for details. WrapperFactoryImpl calls the first native method, nativeInitialize. See nativeAppInitialize() in classes_spec/org/mozilla/webclient/wrapper_native/WrapperFactoryImpl.java for details. Briefly, however, nativeAppInitialize() does the per-application (as opposed to per-window) browser initialization.
The custom app must invoke queryInterface() on the BrowserControl instance to obtain implementation instances of Webclient interfaces. The first interface asked for must be "webclient.BrowserControlCanvas". The custom app must then add() the canvas to its window hierarchy.
Adding the canvas to the display hierarchy generates a WindowControlImpl object. WindowControlImpl, as well as all classes implementing Webclient interfaces, inherit from ImplObject.
Note: WrapperFactoryImpl and BrowserControlImpl, however, do not inherit from ImplObject. WrapperFactoryImpl is not a public implementation class. BrowserControlImpl, however, do not inherit from ImplObject. WrapperFactoryImpl is a public implementation class, but it is special since it is the root of the interface collection.
ImplObject simply defines the common attributes for all Webclient implementation classes. These attributes currently are WrapperFactory and BrowserControl. (See myFactory and myBrowserControl in classes_spec/org/mozilla/webclient/ImplObject.java for details.) The first method of WindowControl, called createWindow(), calls the first per-window native call, nativeCreateInitContext(), which allows the native code to create a "context object" that is passed to all subsequent native methods. It then creates and starts a Java thread, NativeEventThread, and invokes wait() in the main thread. When run, NativeEventThread completes the initialization process, invokes notify() to wake up the WindowControlImpl instance, and goes into an infinite loop. On receiving notify() WindowControl.createWindow() returns. All of this happens on the add() method.
NavigationImpl, CurrentPageImpl, HistoryImpl
After adding the canvas to the custom app window hierarchy, the user may then call BrowserControl.queryInterface() for any other Webclient interfaces. The following classes all have similar, straightforward, implementations: NavigationImpl, CurrentPageImpl and HistoryImpl. These classes all use a native event system, explained later in the descriptions of nsActionEvent and NativeEventThread, to cause propogation of actions to the underlying browser.
The Webclient native method implementations use an event queue to send actions to the browser. The use of an event queue was present in the initial Webclient code written by Kirk Baker and Ian Wilkinson. Using the event queue provides synchronization at the native level via Mozilla monitors. For a description of how this works, see the comments for the nsActionEvent class definition in nsActions.h. The comments there will lead you through the source files related to nsActionEvent subclasses.
NativeEventThread is a java.lang.Thread subclass. It is created as mentioned in WindowControlImpl. There is one instance of NativeEventThread per BrowserControlImpl. NativeEventThread has two purposes:
- to process events from the Java to the browser
- to process events from the browser to Java
The details of sending events from Java to the browser is described elsewhere
in the section on nsActionEvent.
To send an event from the browser to Java:
When the custom app calls EventRegistration.add*Listener(), the corresponding method in EventRegistrationImpl is called. The different add*Listener() methods simply provide type safety. They all call through to NativeEventThread.addListener(), which results in adding the corresponding Mozilla listener type. See addListener() in NativeEventThread. When a Mozilla event occurrs for which the appropriate listener has been added, the native code calls NativeEventThread.nativeEventOccurred(), passing the listener to which the event should be sent and an event-specific long value. nativeEventOccurred() uses Java reflection to determine what kind of WebclientEvent should be created and sent to the listener's eventDispatched() method.
This is a context struct used for holding per-window Mozilla references. There is one instance of this struct per Java WindowControlImpl. The instance is created as a result of calling WindowControl.createWindow().
Note: the instance is destroyed as a result of calling BrowserControl.terminate().
This class implements multiple interfaces needed to embed mozilla. These include nsIWebBrowserChrome, nsIWebProgressListener, and nsIPrompt. This also implements the Listener interfaces nsIDocumentLoaderObserver and nsIDOMMouseListener.
This section deals with private webclient classes that are not part of the core functionality.
The Bookmarks interface uses Mozilla's RDF bookmarks system to provide the cusom app with access to Mozilla bookmarks. The following class diagram describes the bookmarks system.
As expected, the custom app obtains a reference to the singleton BookmarksImpl by invoking queryInterface() on the BrowserControl. Bookmarks provides a way for the custom app developer to show a Swing, JTree-based UI for the bookmarks. When getBookmarks()is invoked on Bookmarks, it in turn invokes nativeGetBookmarks(), wraps a BookmarkEntryImpl instance around the returned int, which is actually an nsIRDFResource, and creates a DefaultTreeModel around the new BookmarkEntryImpl.
This abstract base class provides a Java wrapper to the Mozilla RDF implementation.
This abstract class allows proper garbage collection of RDFTreeNode instances, which have an associated native nsIRDFResource peer. Garbage collection takes place in both Java and XPCOM. It does this by simply providing native methods for XPCOM's AddRef() and Release(). nativeAddRef() can be called from the constructor of a Java subclass. This method isn't used since the AddRef() comes at the native level. nativeRelease() is called from the finalize() method of the Java subclass.
This concrete subclass of RDFTreeNode provides a Properties instance into which the bookmark's meta-data is stored. (NOTE: This is not yet implemented.) Instances of BookmarkEntryImpl are created both by the RDFTreeNode.newRDFTreeNode() and Bookmarks.newBookmarkEntry(). The latter method allows the custom app to add a new bookmark. The app should call Bookmarks.newBookmarkEntry(), fill in Properties and call Bookmarks.addBookmark() with the new bookmark and an option parent BookmarkEntry.
In order to fulfill the TreeNode contract, RDFTreeNode must provide a children() method which returns a java.util.Enumeration of the node's children. This class provides that enumeration; it simply calls through to native RDF methods.