Taming the Lizard with Delphi
-----------------------------
By Dave Murray <irongut at vodafone dot net>


Would you like to display HTML in your applications using an open source
solution? The Gecko Runtime Environment by the Mozilla Foundation can be
embedded in a Delphi application by using the Mozilla ActiveX Control. 
This article shows how to use this control and points out some of the 
differences between it and the IE based TWebBrowser. The accompanying 
source code was written in Delphi 6 but should work in Delphi 5 and 
later. If you want to use the Mozilla ActiveX Control with Delphi 4 you 
also need to read 'Delphi 4 Lizard Taming'.

First some background and terminology. In 1998 Netscape released the 
source code for Communicator, their Internet suite, to the open source 
community and created Mozilla.org. After several years development, the 
project finally released Mozilla 1.0 in 2002. By then Netscape had lost 
the browser wars to Microsoft and been bought by AOL, who repackaged 
Mozilla 1.0 as Netscape 7.0. In 2003, AOL closed its Netscape software 
division and the Mozilla code is now entirely controlled by the 
non-profit Mozilla Foundation. During development the Netscape code was 
completely re-written and the community invented new technologies such 
as XUL, a cross platform UI definition language based on XML, CSS and 
JavaScript. Like Communicator, the Mozilla Suite is comprised of a 
Browser, Mail and Newsreader, HTML Composer and an Address Book. Behind 
these applications is the Gecko Runtime Environment (GRE) - a fast, 
free, standards compliant HTML display engine.

All the Mozilla code is released under an MPL/GPL/LGPL tri-license so 
you can choose the license that suits your needs when using it. For C 
programmers, building applications based on the Mozilla code is easy, 
they can just download the code and start hacking immediately but there 
is also an option for the rest of us. The GRE comes with an ActiveX 
control, written by Adam Lock, which can be used to embed it in other 
applications. This Mozilla Control is written to emulate the familiar IE
based TWebBrowser.

There is no documentation for the Mozilla Control but you can use the 
MSDN documentation for TWebBrowser and ask questions in the 
netscape.public.mozilla.embedding newsgroup.


Installing Mozilla Control v1.4.x
---------------------------------

To install v1.4 we basically have two options, download and install the 
Mozilla Suite or just the GRE itself. I recommend you try the Mozilla 
Suite, it is fast, stable, standards compliant and more secure than IE. 
The original code for this article was written using 
Mozilla/GRE v1.4 20030624 and also tested with v1.4.1 20031008.

* Mozilla Suite  v1.4.1 (via http)
  http://www.mozilla.org/releases/#1.4.1

* Mozilla Suite v1.4.1 (via ftp)
  ftp://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.4.1/
  mozilla-win32-1.4.1-installer.exe

* Gecko Rendering Engine v1.4.1 (via ftp)
  ftp://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.4.1/
  windows-xpi/gre-win32-installer.zip
 
Since v1.4, the control has been part of the GRE rather than the Mozilla
Suite and currently requires extra files that are not part of the 
standard install. Download the support files from Adam Lock's website 
(http://www.iol.ie/~locka/mozilla/gre_app_support14.zip) and unzip them 
to your GRE directory.

e.g. C:\PROGRAM FILES\COMMON FILES\MOZILLA.ORG\GRE\1.4F_2003062408

The control is not registered by the installer so you need to start a 
command prompt, go to the main GRE directory and type 
'regsvr32 mozctlx.dll'. Do not register any other DLLs!


Installing Mozilla Control v1.5+
--------------------------------

Since v1.5, Adam has repackaged the control with the GRE and support 
files in an installer that registers it for you. An additional GRE is 
installed, even if the same version is already on your PC, in a 
directory you specify. I don't like this way of doing things because the
installer requires user input which prevents us creating a seamless 
installer for our own applications. Unfortunately the control no longer 
works correctly with the v1.4 support files package. I plan to work out 
which files are required and in a future article provide instructions 
that will enable the creation of a single installer for the control and 
an application. 

* Mozilla ActiveX Control v1.5
  http://www.iol.ie/%7Elocka/mozilla/MozillaControl15.exe

* Mozilla ActiveX Control v1.6
  http://www.iol.ie/%7Elocka/mozilla/MozillaControl16.exe

* Mozilla ActiveX Control v1.7.1
  http://www.iol.ie/~locka/mozilla/MozillaControl171.exe


Importing the Control into Delphi
---------------------------------

You can now import the control into Delphi in the normal way: Component 
| Import ActiveX Control | Mozilla Control 1.0 Type Library | Install. 
The control will appear on the ActiveX page of the Component Palette as 
TMozillaControl; it doesn't contain its own image so it will use 
Delphi's default one. The archive accompanying this article contains a 
*.dcr file that you can use to replace the generated one for a nicer 
image.


Building a Browser Framework
----------------------------

Since the Mozilla Control is designed to directly replace TWebBrowser, 
I will first describe a browser framework that can be used to 
demonstrate or test either control. The main form will have a main menu 
and be composed of a TPanel at the top that contains a TToolBar, a TEdit
for entering URLs and a TAnimate for the throbber, another TPanel below 
for the browser control and at the bottom a TStatusBar with a progress 
meter (TProgressBar). The status bar needs 2 panels, the first 150 width
for the progress meter and the rest for status messages.

The main menu has the following items:
- File: Open, Save As, Page Setup, Print Preview, Print, Exit
- Edit: Copy, Clear Selection, Select All, Preferences
- Go: Back, Forward, Stop, Reload, Home
- Help: About

The toolbar has the following buttons: Back, Forward, Stop, Refresh, 
Home, Print and Properties. I'm using several TImageList components for 
the main menu and toolbar button images and for the throbber (page 
loading animation) I used 'Delphi6\Demos\CoolStuf\Cool.avi'.


Adding TMozillaControl to a Form
--------------------------------

Adding a TMozillaControl to a form presents us with some unusual 
problems. For a start you can't select it by clicking on it so you have 
to use the Object Inspector or Object Treeview (use this if you need to 
delete it). And, it is not possible to drag or stretch the control so 
you have to change its Top, Left, etc properties in the Object Inspector
to position it. Also, it doesn't have an Anchors property so I recommend
that you place it on a TPanel and set Align := alClient as I have done.

I don't know why the Mozilla Control has these problems (or why it is 
lime green in the IDE) but they are minor inconveniences rather than 
major bugs.


Connecting TMozillaControl to the Framework
-------------------------------------------

Now that the Mozilla Control is correctly placed on the form it is time 
to start connecting it to the rest of the GUI. The methods for the Go 
menu (and buttons) are identical to using TWebBrowser. We just call the 
relevant methods of the control, e.g. mzGecko.GoBack.

TMozillaControl provides overloaded Navigate methods which are identical
to TWebBrowser:

procedure Navigate(const URL: WideString); overload;
procedure Navigate(const URL:WideString; var Flags:OleVariant);overload;
procedure Navigate(const URL: WideString; var Flags: OleVariant; 
                             var TargetFrameName: OleVariant); overload;
procedure Navigate(const URL: WideString; var Flags: OleVariant; 
   var TargetFrameName: OleVariant; var PostData: OleVariant); overload;
procedure Navigate(const URL: WideString; var Flags: OleVariant; 
              var TargetFrameName: OleVariant; var PostData: OleVariant;
                                     var Headers: OleVariant); overload;

We use one of these in TfrmMain.edtAddressKeyDown to browse to the given
URL when Enter is pressed.

The OnStatusTextChange and OnCommandStateChange events work exactly as 
with TWebBrowser. The Text parameter of OnStatusTextChange is displayed 
in the second panel of the status bar. OnCommandStateChange is used to 
enable and disable the Back and Forward buttons and menu items. 

Initially I had some trouble using the OnProgressChange event to control
a progress meter. The code I have used before with TWebBrowser didn't 
work but Sterling Bates suggested an alternative that works perfectly. 
The new code simply shows the progress as a percentage of the maximum. 
To complete this feature the progress is set to 0 in OnDownloadComplete.

All browsers have an animation called a throbber which tells the user 
that a page is loading. We start this animation in the control's 
OnDownloadBegin event and stop it in the OnDownloadComplete event. We 
also use the latter event to ensure that edtAddress shows the correct 
URL.


Open URL Dialog
---------------

Our browser needs a dialog that allows the user to open a URL or file. 
There is only one complication when using TMozillaControl, when opening 
a local file any relative images or links with a backslash in their path
won't load. This is easy to fix, we just need a function that replaces 
any backslash with slash before we attempt to use the URL. I used the 
CharReplace function from the JEDI Code Library (JclStrings.pas) and 
have copied it into the code for the reader's benefit.


Preferences
-----------

Preferences are also pretty simple. I've stored them in global variables
and they are read and written when the dialog is created and closed. I 
used the CharReplace function again to ensure that the start and home 
pages will be valid URLs.

Trying to show the start page in TfrmMain.FormCreate gave an unspecified
error, probably because the control wasn't properly initialised yet. So,
we must do it in TfrmMain.FormShow if the application has just started.


Printing and EOleExceptions
---------------------------

If you've used TWebBrowser before you'll know that printing is an OLE 
command invoked via the IOleCommandTarget interface by calling the 
control's ExecWB method. You'll also know that to avoid an EOleException
we need to initialise the OLE library before we use it and free it when 
we've finished. This is best done in initialization and finalization 
sections. We use this code to Print a page:

  mzGecko.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER);

Initially everything went wrong when I tried Page Setup 
(OLECMDID_PAGESETUP) and Print Preview (OLECMDID_PRINTPREVIEW). Both 
commands caused an exception - EOleException: Trying to revoke a drop 
target that has not been registered. In order to find out what was going
wrong I talked to Adam Lock who recommended I look at the source code. 
I don't do C but he gave me a few pointers and I bravely dived into 
MozillaBrowser.h. :) I immediately noticed that OLECMDID_PRINTPREVIEW is
not defined and reported it as bug #214884. 

OLECMDID_PAGESETUP is defined in MozillaBrowser.h but gave the same 
exception so I reported it as bug #225045. Since then, Adam Lock has 
looked into the problem. He expected OLECMDID_PAGESETUP to show a dialog
with the message 'No page setup yet!'. The problem was a bug in the way 
the control tests the second parameter passed in ExecWB. Prior to v1.7, 
the answer is to pass OLECMDEXECOPT_DODEFAULT instead of 
OLECMDEXECOPT_PROMPTUSER. Adam has now corrected this and implemented a 
Page Setup dialog in v1.7. The dialog has all the expected options and a
nice headers and footers page but measurements are in inches although 
the dialog says millimeters. Also, these settings are not saved so they 
have to be entered every time an application is run. These problems are 
fairly minor and I hope to see them fixed soon.

Another File menu command gave me the same exception, Save As. The 
problem when calling OLECMDID_SAVEAS is the second parameter again. This
has the same workaround prior to v1.7 and has also been fixed.


The Edit Menu
-------------

Several edit menu commands are also invoked via IOleCommandTarget - 
Copy (OLECMDID_COPY), Select All (OLECMDID_SELECTALL) and Clear 
Selection (OLECMDID_CLEARSELECTION). The first two commands work as per 
TWebBrowser but with Clear Selection we get an EOleException again. 
Looking at the source, OLECMDID_CLEARSELECTION is not defined in 
MozillaBrowser.h; Bugzilla #214884.


Conclusion
----------

The Mozilla Control can be used to display HTML and provide basic 
browsing functionality in a Delphi application. It is designed to be a 
close copy of TWebBrowser to make it easier to use but is missing some 
functionality and has a few quirks of its own. Although development of 
the control is slow there is work being done and some of the bugs have 
been fixed recently. There are two main problems that prevent us 
providing some basic features:

  * Print Preview command doesn't exist; Bugzilla #214884
    http://bugzilla.mozilla.org/show_bug.cgi?id=214884

  * Clear Selection command doesn't exist; Bugzilla #214884
    http://bugzilla.mozilla.org/show_bug.cgi?id=214884

The latest version, the Mozila ActiveX Control v1.7.1, is based on the 
Mozilla v1.7.2 codebase. This includes the recent shell: protocol 
security patch as well as many other speed and stability improvements. 
I recommend everyone to use this version in their applications.

In future articles I plan to explain how to automatically install the 
Mozilla Control with an application and explore more complex features 
like DOM support. The  code accompanying this article is a basic 
browser framework that can be used to demonstrate or test the Mozilla 
Control. Feel free to use and abuse it as you wish.


References
----------

Mozilla Foundation
http://www.mozilla.org

Mozilla Control
http://www.iol.ie/~locka/mozilla/mozilla.htm

Mozilla Control Source Code
http://lxr.mozilla.org/seamonkey/source/embedding/browser/activex/src/
control/

Bugzilla
http://bugzilla.mozilla.org/

Newsgroup
netscape.public.mozilla.embedding

MSDN WebBrowser Reference
http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/
webbrowser/reference/objects/webbrowser.asp

Project JEDI
http://www.delphi-jedi.org/

Mozilla-Delphi Project
http://delphi.mozdev.org/

Irongut's Delphi Pages
http://www.paranoia.clara.net/

