2016年3月20日星期日

Mozilla ActiveX Control


Hi Guys

An interesting question which got me thinking.......

There is a Mozilla ActiveX Control available to download from here:
http://www.iol.ie/~locka/mozilla/control.htm

If you follow the instructions on that website it is pretty easy to install and it is compatiable with VBA (despite their website not listing it as compatible). The only way I have managed to use this control so far (in my 30 seconds of testing) is to create a user form with a huge Mozilla control and then use the following code for that form :
Code:
Private Sub UserForm_Activate()
Me.MozillaBrowser1.Navigate ("http://www.mrexcel.com/board2/viewtopic.php?p=1182388")
End Sub
This hasn't actually opened Firefox, it merely used a Firefox control on an Excel form.

Food for thought?

Andrew


Introduction

Motivation

"Wouldn't it be great if the Mozilla browser engine were an Active control that could be embedded as in applications?"
That's a question that myself and other had asked on the Mozilla groups soon after the Mozilla project began. And further:
"Wouldn't it be great if the Mozilla control used the same API as the Internet Explorer control?"
The aim of this project is to be both of these things.

Why?

Previous versions of Netscape Communicator/Navigator were arguably superior to IE as day to day browsers but they suffered through their immediate usability and modularity. Although the Netscape browser was great as a standalone application, it wasn't possible to utilize that functionality in third party applications. On the other hand, Internet Explorer shipped with an ActiveX control which allowed exactly that ability.
Take a look at some of the applications that already use the IE control:
There are hundreds of others apps, some commercial and probably many more running in intranets all over the world.
So what would it take for developers to use the Mozilla control as opposed to the IE control?
  • An API close as possible to Internet Explorer's for ease of porting
  • Freely available source code to allow for bug fixing, customization, etc.
  • A very small distributable - 3-4Mb (for a download containing just the Gecko embedding engine and not the full Mozilla) compared to the 10Mb+ required for IE
  • State of the art rendering - exploit the speed and standards compliance of the Gecko rendering engine
  • No more nasty IE license - have you read it

ActiveX Control Implementation

Not just a similar API

An identical one! That's right, the Mozilla control will implement the IWebBrowser and DWebBrowserEvents interfaces that Microsoft have already defined for Internet Explorer.
Since the Mozilla control implements exactly the same API, it will mean that developers can take existing IE code and port it, sometimes in a matter of minutes!
The only modification required may be to replace the line that creates the control of type CLSID_WebBrowser with one of CLSID_MozillaBrowser. VB developers must delete the IE control from their project and insert a Mozilla one with the same instance name.
Let's look at what the IE control exposes interface-wise to the world:
  • IWebBrowser - containing methods such as Navigate, GoBack, GoForward, Refresh
  • IWebBrowser2 and IWebBrowserApp - Extra executable specific methods that Mozilla partially implements
  • DWebBrowserEvents - containing outgoing events that the control fires when the user or browser does something the application should know about such as finishing page loading or clicking on a hyperlink
  • DWebBrowserEvents2 - More outgoing events introduced by IE4.0
Obviously an application will not be so portable if it is viewing IE specific HTML such as VBScript, ActiveX controls or DHTML. In this case, the application may need to modify the HTML content in order to be compatible.

Why not just use native Gecko API calls?

Gecko is a very powerful set of APIs implemented in XPCOM (COM-like) set of interfaces. Unfortunately, it is only suitable for C++ and has a steep learning curve that may not appeal to some. So if you're a VB or Delphi programmer, your only way of using Mozilla is through the control. Also, it's the easiest way to port code that already uses IE.
The Mozilla control provides the wrapper code that converts between the IWebBrowser and the internal Gecko engine interfaces and catches Gecko events and fires the appropriate ActiveX events. Since it is a true ActiveX control, it can be shipped without headers or source. All the programming information is stored in the DLL as a type library and is therefore language independent.

Internet Explorer DOM support

The IE wrapper control will implement a simple DOM using the same COM interfaces as you find in Internet Explorer 4.0, i.e.
  • IHtmlDocument2
  • IHtmlElement
  • IHtmlElementCollection
  • Etc.
This enables the client application to parse the contents of a page programmatically and even modify the page.
It is important to note that the Mozilla control will not have a complete implementation of the DOM since it is extremely complex. Here is what you can expect:
  • Implementation of most common element types
  • Retrieve element info such as class name, id etc.
  • Traversal functions. Parse the DOM using methods such as get_children, get_all, get_parent etc.
  • Attribute setting and getting, e.g. setAttribute, getAttribute etc.
  • Simple modifications, for instance to be able to set or get text from text elements
  • Integration with the ActiveX Plug-in support (if present) so that ActiveX objects in the HTML are available to clients.
What you shouldn't expect:
  • Exact DOM mapping between IE and Mozilla. The IE wrapper control uses the Mozilla (and therefore W3C DOM level 1 standard) for its element hierarchy and not the IE one.
  • Implementation of complicated methods. By complicated I mean ones where there is no direct mapping between the IE method and the Mozilla equivalent.
The interfaces for Internet Explorer 5.0 are considerably more complicated and I have not reached a decision whether I should attempt to implement them in Mozilla.

Progress so far

Okay, enough of the blurb, here's the current state of play.
The following interfaces have been implemented satisfactorily:
  • IWebBrowser (basic web browsing functions for navigation and son on)
  • IWebBrowserApp (some extended functions for IE, the application)
  • IWebBrowser2 (some more functions for IE, the application)
  • DWebBrowserEvents (basic navigation events)
  • DWebBrowserEvents2 (more events, mainly for the IE app)
Many of the methods on the IWebBrowserApp, IWebBrowser2 and DWebBrowserEvents2 interfaces are implemented by dummy, no-operation code since they don't have meaning when the browser is running as a control.
In the current state of the Mozilla control, it is possible to run the IEPatcher tool on existing binaries and have them run using the Mozilla control.

Implementation details

The Mozilla control is implemented in ATL. Anyone who has tried to write complex COM objects in raw C++ or with the hateful MFC will know exactly the reasons why I chose ATL!
Amongst the reasons for using ATL are:
  • MFC produces larger binaries and requires the MFC DLL to be distributed for the control to work.
  • Like most things in MFC, the COM support is a hack and not designed very well.
The main class is CMozillaBrowser which is the ActiveX control itself. It derives from CComControl plus IWebBrowser and a host of ATL implementation classes. It is a window only control and hooks the WM_CREATE and WM_DESTROY events to create and destroy the Gecko web browser class.
When an IWebBrowser method is called, a piece of code in CMozillaBrowser converts the parameters to their nsIWebWidget equivalents to perform the actual operation.
For outgoing events, everything goes in the opposite direction. During creation of the Gecko web browser, CMozillaBrowser registers event listeners implemented by the CWebBrowserContainer class with Gecko. Gecko calls these methods when a page is loaded, for progress notifications and son and CWebBrowserContainer fires the equivalent IE control events out via the CProxyDWebBrowserEvents template that CMozillaBrowser inherits from.

Get the binaries!

Standalone Mozilla ActiveX Control installers. Note these contain just the control and the parts of Mozilla Gecko it needs to function. It does not contain the full Mozilla browser.
Version
Mozilla ActiveX Control 1.7.12
Mozilla ActiveX Control 1.7.7
Mozilla ActiveX Control 1.7.1
Mozilla ActiveX Control 1.6
Mozilla ActiveX Control 1.5
The Mozilla executable (including the control) is built at Mozilla.org on a nightly basis. You can find information about the latest builds here.
Please note that nightly builds are automated and represent work in progress. If this bothers you consider using one of the more stable milestone releases.

Get the source!

The ActiveX source code is part of the Mozilla source tree. Tarballs are usually to be found here. All the code is contained in the subdirectory mozilla/embedding/browser/activex/src.
Source code for IEPatcher, VBrowse and CBrowse are contained in the mozilla/embedding/browser/activex/tests subdirectory. The source is also online courtesy of LXR.

Building it

The control is built as part of the normal Mozilla Win32 build process. Follow the instructions on the Mozilla.org website to build Mozilla:
  1. Instructions to obtain the source code
  2. Instructions to build Mozilla on Win32
Refer to Mozilla.org or netscape.public.mozilla.builds if you have difficulties building Mozilla.
Note: You cannot build the control standalone! The control depends on libraries and header files that you only get from building the whole Mozilla! 

A note about compilers

The ActiveX control is particularly finicky about compilers since it uses the ATL libraries which have been known to clash with certain Platform SDKs. Please see below for certain notes to avoid problems.

DevStudio 7.1 (.NET 2003)

I don't own .NET 2003 but I've had no reports of issues and assume it works.

DevStudio 7 (.NET 2002)

There are no special requirements for this build except that your configuration matches that specified by Mozilla build instructions (GNU make, cygwin etc.).

DevStudio 6

There are no special requirements for DevStudio 6 except that you apply the latest service pack (at least SP5). You do not need to install a Win32 Platform SDK but if you have I recommend that you remove references to it from your PATH, INCLUDE and LIB environment variables before commencing a build. 

DevStudio 5

Note: Nov 2003 - I have absolutely no idea if it still builds in DevStudio 5. Miracles happen, but you're on your own I'm afraid.
Note: DevStudio 5 is no longer supported, but these old instructions might still be valid.
The default installation of DevStudio 5 is broken, especially with regard to its ActiveX and IDL support. Therefore, you need to do a little work before you can build the control:
  • Apply the Devstudio 5 Service Pack 3
  • Install the Win32 Platform SDK (January 1998 or later). Only the base set of files is required, i.e. those installed by iBLDENV.Exe
  • Ensure your LIB, INCLUDE and PATH environment variables all reference the Win32SDK paths before their DevStudio equivalents. For example:
      set PATH=c:\winsdk\bin;c:\progra~1\devstudio\bin;%PATH%
          set INCLUDE=c:\winsdk\include;c:\progra~1\devstudio\include;%INCLUDE%
          set LIB=c:\winsdk\lib;c:\progra~1\devstudio\lib;%LIB%

Installing it

Installation should be a straightforward procedure:
  1. Install or build mozilla
  2. Open a DOS prompt, change to your Mozilla bin directory (e.g. "cd c:\mozilla\bin")
  3. Type "regsvr32 mozctlx.dll"
Note that building Mozilla will usually register the control for you unless you have set MOZ_NO_ACTIVEX_REGISTRATION to disable this behaviour.
If regsvr32.exe is not in you path use the "Find Files..." facility of 95/98/NT to locate it and run it using its the full path.
Do not register mozctl.dll! The new mozctlx.dll now contains all the PATH fixup magic that ensures the control works correctly.

Testing it

The Mozilla control should work any ActiveX control compatible container. This includes: 
  • Visual Basic
  • Delphi
  • Visual C++
  • ActiveX Test Container (it comes with DevStudio)
  • Internet Explorer (!)
Notice the last one? That's right! Since IE HTML pages can contain controls, you can test if your Mozilla control works, by following this link. This take you to a page containing simple browser application using the Mozilla control that runs from IE.
Most of my test work is done with an application called CBrowse. This is a C++ application that can be found in the Mozilla source code tarball.

Using the control

The control is a standard ActiveX component and can be used from any development environment which supports ActiveX. The sections below show you how to use the control in popular environments.
  1. Visual Basic.NET
  2. Visual Basic 6
  3. Visual C++
  4. Delphi
You may also like to read about the IE patcher
As Mozilla has an identical API to IE, you can program it using the documentation for Microsoft's WebBrowser - Reusing the WebBrowser Control

Visual Basic.NET

The Mozilla Browser control should be usable from any automation control container. This includes Visual Basic .NET, so follow these steps to add the control to your VB project:
  • Install the control / or compile it and ensure it is registered.
  • In the "View" menu, click "Toolbox"
  • Click the "Components" tab
  • Right-click anywhere in the "Toolbox" and click "Customize Toolbox..."
  • In the "COM Components" tab, check the "MozillaBrowser Class" and click "OK"
  • The Mozilla Browser control should now appear as "Browser" in the "Toolbox" for insertion into any application. (Click and drag onto your form)

Visual Basic 6

The Mozilla Browser control should be usable from any automation control container. This includes Visual Basic, so follow these steps to add the control to your VB project:
  • Install the control / or compile it and ensure it is registered.
  • Right mouse over the VB control bar and select "Components...".
  • Choose "MozillaControl 1.0 Type Library" from the list of controls
  • The Mozilla Browser control should now appear in the toolbar for insertion into any application
Once the control is inserted, you should be able to directly call the events, methods and properties it exposes. The latest control source contains an example VB project called VBrowse.
Note: Save your project often! Bugs in the alpha-quality Mozilla will crash your development environment and will wipe out any unsaved work you may have.

VBrowse test harness

This is a test application I have written in Visual Basic to test the Mozilla control (and in future compare it to the IE control). It takes the form of a simple web browser app which should increase in functionality as the control becomes more complete. To use it, simply run the executable (or hit play from VB) and then use it like a web browser.
Here's a picture!

Porting a VB project from IE control

  1. Launch VB and load the project to port.
  2. Select the menu "Project|Components..." and from the dialog, check the "MozillaControl 1.0 Type Library"
  3. Load each form the in the project containing the IE control.
  4. Delete each IE browser control in the project and replace it with a Mozilla control, ensuring the variable name is the same for the old and new controls.
  5. Run

Visual C++

ActiveX controls are slightly more tricky to use from C++, however here are 3 ways you can use the control.

Subclassing CHtmlView

MFC provides a CHtmlView class which may be used to instantiate the Internet Explorer control. Since the Mozilla control is API compatible, the steps are practically identical as using CHtmlView, except for overriding the Create method.
a) Edit stdafx.h and add "#include <afxhtml.h>" to pull in the CHtmlView class.
b) Derive your view class from CHtmlView and replace all calls to CView in your class to CHtmlView, e.g. in the IMPLEMENT_DYNCREATE macros and elsewhere.
c) Override the default Create method of CHtmlView with your own:
In the .h file add:
public:
    virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
    DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID,
    CCreateContext* pContext = NULL);
In the .cpp file add:
BOOL CHtmlView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
      DWORD dwStyle, const RECT& rect, CWnd* pParentWnd,
      UINT nID, CCreateContext* pContext)
{
    // create the view window itself
    m_pCreateContext = pContext;
    if (!CView::Create(lpszClassName, lpszWindowName,
    dwStyle, rect, pParentWnd, nID, pContext))
    {
        return FALSE;
    }

    AfxEnableControlContainer();

    RECT rectClient;
    GetClientRect(&rectClient);

    const CLSID CLSID_MozillaBrowser = { 0x1339B54C, 0x3453, 0x11D2,
        { 0x93, 0xB9, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00 } };

    // create the control window
    // AFX_IDW_PANE_FIRST is a safe but arbitrary ID
    if (!m_wndBrowser.CreateControl(CLSID_MozillaBrowser, lpszWindowName,
        WS_VISIBLE | WS_CHILD, rectClient, this, AFX_IDW_PANE_FIRST))
    {
        DestroyWindow();
        return FALSE;
    }

    LPUNKNOWN lpUnk = m_wndBrowser.GetControlUnknown();
    HRESULT hr = lpUnk->QueryInterface(IID_IWebBrowser2, (void**) &m_pBrowserApp);
    if (!SUCCEEDED(hr))
    {
        m_pBrowserApp = NULL;
        m_wndBrowser.DestroyWindow();
        DestroyWindow();
        return FALSE;
    }

    return TRUE;
}
Note that this code is practically identical to the code in the CHtmlView::Create method, the only change is that  CLSID_MozillaBrowser is defined and supplied to the call to CreateControl instead of CLSID_WebBrowser (IE).
d) In your view's OnInitialUpdate add a line such as this to see it working!
Navigate2(_T("http://www.mozilla.org/"), 0, _T(""));
Important Note: The CHtmlView::Navigate2 class calls IWebBrowser::Navigate with a target frame variant containing VT_BSTR and a NULL string value. Versions of the Mozilla control prior to Mozilla 1.3 did not like this and would crash, so specify the target as _T("") instead of NULL.
Here is a sample application that uses CHtmlView.

Using the Add Components dialog

The MFC CWnd class has built-in site control support and an Add Components wizard that generates a wrapper class from any installed control.
To use the Mozilla control from DevStudio 6:
  1. Create a new MFC application project.
  2. Choose "Project|Add To Project|Components and Controls..." from the menu.
  3. From the dialog, select "Registered ActiveX Controls"
  4. Locate the "Mozilla Browser Class" icon and double-click on it, confirming that you wish to insert the control.
  5. From the next dialog, choose a sensible name for the class such as "CWebBrowser".
  6. Open your apps dialog in the resource editor and insert a Mozilla browser using the palette toolbar.
  7. Right click on the browser and select ClassWizard to create a variable to represent the browser (e.g. m_Browser).
  8. Call methods on the new browser variable to call the browser class.
  9. Use the ClassWizard to create event handlers.
Here is a sample application generated using the wizards.

CControlSite

If you're a COM expert / purist who hates wrappers then you may prefer to use the CControlSite class that is part of the Mozilla control project itself. This is a class for hosting any ActiveX control including the Mozilla control. Host the control in the site and control it via its IWebBrowser2 interface and the usual event sink mechanisms.
The advantage of using this class is that it is based on ATL and not MFC and is therefore lighter and suitable for non-MFC projects. You don't get wizards to help you of course, but the class is much easier to pull apart and understand.
The CBrowse sample application demonstrates how this class may be used.

Porting a VC++ project from IE control

Most C++ projects that use the IE control do so via a wrapper class. This in turn usually wraps the MFC CWnd class, providing stub functions for creating the browser and calling methods upon it.
  1. Locate the line that creates the IE control.
  2. Replace the reference to CLSID_Browser with CLSID_MozillaBrowser
  3. Declare CLSID_MozillaBrowser (see below)
  4. Compile
  5. Run
static const CLSID CLSID_MozillaBrowser=
{ 0x1339B54C, 0x3453, 0x11D2,
    { 0x93, 0xB9, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00 } };

Delphi

Thanks to Nick Bradbury & Irongut (Dave Murray) for this section!
And here is a slightly older brief rundown on how to use the control from Delphi. Follow these steps to install and register the Mozilla control on your system
  • Select "Import Type Library" from Delphi's Project menu
  • Choose "MozillaControl" in the type library list and click OK
  • Delphi's ActiveX VCL toolbar should now contain a "MozillaBrowser" component
You can now drop the MozillaBrowser control onto a Delphi form.
The dbrowse application demonstrates a simple browser written in Delphi.
This is another simple project that demonstrates how to use the Mozilla control. Note that this demo dynamically creates the browser object

Using the IE patcher tool

IEPatcher is a tool to scan an executable or DLL and patch it to replace instances of the IE control with the Mozilla control. Since both controls are binary compatible, this is just a matter of replacing the CLSID_WebBrowser with CLSID_MozillaBrowser. If you have built the Mozilla browser, try running the patcher on an app that uses IE and see if it works!
Note: As a safety feature, the patcher does not let you overwrite the original file, but be careful all the same!
Here's a picture!

没有评论:

发表评论