NOTE:
This project is no longer being maintained: it was developed for my masters thesis, which was completed in early 1997. I still, however, welcome any questions or comments that people may have.

[Home] [ToC] [Up] [Prev] [Next]


iHTML Architecture

Language Developer Tutorial

This section describes how to implement an iHTML language module, by examining the source code to implement a simple language module. The example implements a module that displays any text data type as a scrollable text view. The content type description for this module is:

Name: text
Content-type: text/*

In order to keep the example as simple as possible, the module does not implement document script execution. Such a situation is typical for modules that display traditional data types, as scripts executing in a document context do not include a graphics context in which data can be displayed. In addition, the module makes use of low-level Xt widgets for displaying the actual text. Because of this, it needs to be modified to work with browsers running under different platforms, such as Macintosh or Windows.


Module Implementation

Definitions

The first thing the module must do is include the header files for the parts of the X system it uses. This includes the basic X headers, along with the X Athena widgets needed to make a scrollable text area.

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xmu/Converters.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/AsciiText.h>

The iHTML header file declares a number of types used by the system. Many of these types are defined as a black-box (void*) by the library, in order to hide their implementation. However, individual components need define the types they use in their internal implementation. Programs including the iHTML header file can conditionally override the black-box definitions with their own, to have access to the parts of the system they implement.

Here, the language module defines those types that are associated with the language component; the #define statements tell the system which ones it has defined, so that other header files do not later try to redefine them. (The structures referred to here are defined later.)

#define TYPE_IHLangCode 1
typedef struct ih_int_lang_code_rec* IHLangCode;
#define TYPE_IHLangEnv 1
typedef struct ih_int_lang_env_rec IHLangEnv;
#define TYPE_IHWidgetObj 1
typedef struct ih_int_widget_obj_rec* IHWidgetObj;

One of the types that the browser component defines represents the underlying operating system's user interface objects it uses to construct an applet's display context on a document. Because the language module assumes that it is running under X, it knows that this type is a Widget and can use it to get low-level access to the display.

#define TYPE_IHWidgetRaw 1
typedef Widget IHWidgetRaw;

The module now includes the iHTML header file, which defines the interface to the rest of the system. In addition to the interface, the header file also defines a number of macros that will be used later in the module:

_NO_PROTO
This macro is defined if the host C compiler does not support ANSI function definitions.
bug()
If debugging is turned on, this macro results in a printf-style string being sent to stderr.
D()
This macro executes its argument if debugging is turned on.
IH_PROTO
This is used to construct function prototypes that are removed in compilers that do not support them,
MARK
If debugging is turned on, this macro writes information about the current line number and source file to stderr.
#include "IHScript.h"

Structures

A language module defines various data structures that are part of the iHTML system. The first is the IHLangCode, which contains state information for a single code block executing in the module.

A code block is not necessarily an actual program -- it is simply the context for some data that has been retrieved with an HTML <OBJECT> tag. For this text module, it is any ASCII text. The IHLangCode type keeps track of the width and height specified in the initial <OBJECT> tag, and the widgets being used to display the text.

struct ih_int_lang_code_rec {

  /* Fields initialized when code block is first allocated. */
  int width, height;                /* The requested applet size. */

  /* Fields initialized each time execution is started. */
  IHWidgetObj obj;                  /* Top-level widget interface. */
  Widget viewport;                  /* The scrollable area. */
  Widget text;                      /* The text being displayed. */
};

The IHLangEnv type contains environment information associated with a single browser document. This includes global information used by all code blocks executing in that document, and the information needed by the document scripting context.

The iHTML system itself defines a common public part of this structure, which must appear at the front of a language module's internal definition of the IHLangEnv.

struct ih_int_lang_env_rec {
  struct ih_lang_env_rec env;  /* Subclass from iHTML's language env. */

  /* Static variables that are made at IH_PyAllocEnv() time. */
  IHEvent* query_evt;          /* Used to determine document dimension. */

  /* Dynamic variables that are made at each run of document script. */
  /* (none -- module does not implement a document context.) */
};

The graphical user interface managed by the iHTML system is composed of two parts. The IHWidgetRep is a browser-side object that defines a display context and low-level drawing operations. The second part, IHWidgetObj, is implemented by the language module and encapsulates the intelligence associated with that display context.

This language module goes directly to the operating system for rendering in its display. It only defines a simple IHWidgetObj, which is composed of a single child X Widget; the IHWidgetObj ensures that its dimensions match that Widget's dimensions.

struct ih_int_widget_obj_rec {
  IHWidgetRep rep;                  /* Browser widget associated with. */
  IHWidgetRaw raw;                  /* Raw OS-level widget object.  For the
				       Xt interface, this is a Widget obj. */
  Dimension width, height;          /* Preferred dimensions. */

  Widget child;                     /* X-level child widget. */
};

Module Interface

The public interface to a language module is defined by a set of structures that supply general information about the module and provide access to the module's functionality.

First, a #define is cleared, indicating that the module does not implement a document context. Setting the macro will affect how later portions of the code operate.

#undef DOCUMENT_CONTEXT

Before defining the interface structures, the functions they reference must be declared. Note that these function names are not directly visible outside of the module, but are accessed through the structures defined later on.

static void            LM_Initialize       IH_PROTO( (void) );
static void            LM_Terminate        IH_PROTO( (IHLanguage* lang) );
static IHLangEnv*      LM_AllocLangEnv     IH_PROTO( (IHLangEnv* le) );
static void            LM_FreeLangEnv      IH_PROTO( (IHLangEnv* env) );
static int             LM_ExecLangEnv      IH_PROTO( (IHLangEnv* le,
						      IHModuleInfo* main) );
static void            LM_StopLangEnv      IH_PROTO( (IHLangEnv* le,
						      IHModuleInfo* main) );
static IHModuleInfo*   LM_AllocModule      IH_PROTO( (IHModuleInfo* mi) );
static void            LM_FreeModule       IH_PROTO( (IHModuleInfo* mi) );
static IHEmbeddedInfo* LM_AllocEmbedded    IH_PROTO( (IHEmbeddedInfo* ei) );
static void            LM_FreeEmbedded     IH_PROTO( (IHEmbeddedInfo* ei) );
static int             LM_ExecEmbedded     IH_PROTO( (IHEmbeddedInfo* ei) );
static void            LM_StopEmbedded     IH_PROTO( (IHEmbeddedInfo* ei) );
static int             LM_HandleEvent      IH_PROTO( (IHLangEnv* env,
						      IHEvent* event) );
#if DOCUMENT_CONTEXT
static HTMLNode        LM_Markup_HeadNode  IH_PROTO( (IHMarkup markup) );
static HTMLNode        LM_Markup_TailNode  IH_PROTO( (IHMarkup markup) );
static void            LM_Markup_SetHead   IH_PROTO( (IHMarkup markup,
						      HTMLNode node) );
static void            LM_Markup_Ref       IH_PROTO( (IHMarkup markup) );
static void            LM_Markup_Deref     IH_PROTO( (IHMarkup markup) );
#endif

The IHLanguageFunc structure defines the functional interface to the module. It is composed of pointers to the various functions that make up its public interface.

If a module does not implement a document context, it does not need to define its HTML parse tree functions. Instead, it may set those to NULL, informering the rest of the system of this fact. Similarly, setting the terminate function to NULL will keep the system from unloading the module when it is no longer in use.

static IHLanguageFunc lang_functions = {
  LM_Terminate,
  LM_AllocLangEnv, LM_FreeLangEnv, LM_ExecLangEnv, LM_StopLangEnv,
  LM_AllocModule, LM_FreeModule,
  LM_AllocEmbedded, LM_FreeEmbedded,
  LM_ExecEmbedded, LM_StopEmbedded,
  LM_HandleEvent,
#if DOCUMENT_CONTEXT
  LM_Markup_HeadNode, LM_Markup_TailNode, LM_Markup_SetHead,
  LM_Markup_Ref, LM_Markup_Deref
#else
  NULL, NULL, NULL, NULL, NULL
#endif
};

The IHLanguage structure is the top-level interface to the module. It includes a pointer to its functional interface, and various other pieces of information.

static IHLanguage lm_language = {
  &lang_functions,
  MODULE_VERSION, MODULE_REVISION,
  MODULE_SUBMAKE,                  /* Version info */
  MODULE_BUILD_DATE,               /* (char*) build date */
  "text",                          /* Name of this module */
  sizeof(IHLangEnv)                /* Size of module's language env. */
};

Finally, this module declares a number of internal variables that it uses to store global state information.

/* The name of the browser executable. */
static char* lm_cache_argv0 = NULL;

/* The directory name from which we were loaded. */
static char* lm_name = NULL;

/* The path to find external files for this language module. */
static char** lm_path = NULL;

Initialization and Termination

The initialization function is the only publically defined symbol in a module. It takes two parameters: browser is the name of the executable file the browser was loaded from, and language is the name of the language path the module was loaded from. If the initialization is okay, it returns a pointer to the module's IHLanguage; otherwise, it returns NULL.

The initialization function can be defined with a couple of symbol names. The iHTML system first looks for a symbol named IHTML_language, where language is the name of the language path from which the module was loaded. If that symbol is not found, it then falls back on the symbol IHTMLinterface, as is used here.

IHLanguage*
#ifdef _NO_PROTO
IHTMLinterface(browser, language)
     char* browser;
     char* language;
#else
IHTMLinterface(char* browser, char* language)
#endif
{
  char* rawpath;

  MARK;
  D(bug("Initializing language: %s\n",language));

  if( !(lm_name=strdup(language)) ) {
    BR_ReportError(NULL,"Unable to get memory for name '%s'\n",language);
    return NULL;
  }
  MARK;
  lm_cache_argv0 = browser;

  /* Set up the module path. */
  MARK;
  lm_path = BR_ParsePath(rawpath=BR_GetBasePath(language,NULL),language);
  MARK;
  if( !rawpath ) {
    BR_ReportError(NULL,
                   "Unable to construct iHTML search path for language '%s'\n",
		   language);
    free(lm_name);
    lm_name = NULL;
    return NULL;
  }
  MARK;
  if( !lm_path ) {
    BR_ReportError(NULL,"Unable to parse iHTML search path:\n%s\n",rawpath);
    free(rawpath);
    free(lm_name);
    lm_name = NULL;
    return NULL;
  }
  free(rawpath);

  MARK;
  D(bug("Returning lang=%x, fn=%x, func=%x\n",&lm_language,lm_language.Fn,
	LM_AllocLangEnv));
  return &lm_language;
}

A module's Term() function is called by the system prior to unloading the entire module from memory.

static void
#ifdef _NO_PROTO
LM_Terminate(language)
     IHLanguage* language;
#else
LM_Terminate(IHLanguage* language)
#endif
{
  MARK;
  D(bug("Terminating language: %s\n",lm_name));

  if(lm_path) BR_FreeParsePath(lm_path);
  if(lm_name) free(lm_name);
}

HTML Parse Trees

Modules that include a document context should define functions for handling HTML parse trees. To do this, they define an IHMarkup type that stores information about the first and last nodes in a tree and a reference count, and define the module's IHMarkup_HeadNode(), IHMarkup_TailNode(), IHMarkup_SetHead(), IHMarkup_Ref() and IHMarkup_Deref(), functions to manipulate this type.

#if DOCUMENT_CONTEXT

static HTMLNode
#ifdef _NO_PROTO
LM_Markup_HeadNode(obj)
     IHMarkup obj;
#else
LM_Markup_HeadNode(IHMarkup obj)
#endif
{
  return NULL;
}

static HTMLNode
#ifdef _NO_PROTO
LM_Markup_TailNode(obj)
     IHMarkup obj;
#else
LM_Markup_TailNode(IHMarkup obj)
#endif
{
  return NULL;
}

static void
#ifdef _NO_PROTO
LM_Markup_SetHead(obj,node)
     IHMarkup obj;
     HTMLNode node;
#else
LM_Markup_SetHead(IHMarkup obj,HTMLNode node)
#endif
{
}

static void
#ifdef _NO_PROTO
LM_Markup_Ref(obj)
     IHMarkup obj;
#else
LM_Markup_Ref(IHMarkup obj)
#endif
{
}

static void
#ifdef _NO_PROTO
LM_Markup_Deref(obj)
     IHMarkup obj;
#else
LM_Markup_Deref(IHMarkup obj)
#endif
{
}

#endif

Event Handling

Every module must define the HandleEvent() function to handle system IHEvent objects that are sent to it. When called with an event, the function needs to distinguish whether it occurred in a document and applet context, and route it to the correct part of the module.

The module does not implement a document context, so it simply returns zero for any events it receives in that context, indicating it did nothing with them. Otherwise, it sends the event to its widget event handling code.

static int
#ifdef _NO_PROTO
LM_HandleEvent(env,event)
     IHLangEnv* env;
     IHEvent* event;
#else
LM_HandleEvent(IHLangEnv* le,IHEvent* event)
#endif
{
  MARK;
  D(bug("Handling event for '%s'\n",lm_name));

  /* Sanity checks. */

  if( !le ) {
    BR_ReportError(NULL,
                   "LM_HandleEvent() for '%s': IHLangEnv is NULL.\n",
                   lm_name);
    return 0;
  }

  if( !event ) {
    BR_ReportError(NULL,
                   "LM_HandleEvent() for '%s': IHEvent is NULL.\n",
                   lm_name);
    return 0;
  }

  if( le->env.language != &lm_language ) {
    BR_ReportError(NULL,
                   "LM_HandleEvent() for '%s': Not for correct language.\n",
		   lm_name);
    return 0;
  }

  MARK;

  if( event->class == IECLASS_DOCUMENT_ID ) {
    /* Process document events. */
    return 0;

  } else if( event->class == IECLASS_WIDGET_ID ) {
    return widget_event(event,(IHWidgetObj)event->object);

  }

  return 0;
}

Widget Objects

The implementation of widget objects is a fundamental part of most language modules. These determine how the user interacts with the module and what information it displays in the document.

This module defines a number of private functions for managing these objects. The first deallocates an IHWidgetObj and any X widgets attached to it.

void
#ifdef _NO_PROTO
free_widget(obj)
     IHWidgetObj obj;
#else
free_widget(IHWidgetObj obj)
#endif
{
  if( obj ) {
    IHMemberValue set[] = {
      { IWM_WIDGETOBJ_ID, 0 },
      { IWM_NULL_ID, 0 }
    };
    if( obj->rep ) BR_SetWidgetMembers(obj->rep,&set[0]);
    if( obj->child ) {
      XtDestroyWidget(obj->child);
    }
    free(obj);
  }
}

The next function allocates a new IHWidgetObj object, attaching it to a browser-side IHWidgetRep object. In the process, it queries the browser-side widget for its default dimensions.

IHWidgetObj
#ifdef _NO_PROTO
alloc_widget(widget)
     IHWidgetRep widget;
#else
alloc_widget(IHWidgetRep widget)
#endif
{
  IHWidgetObj obj = malloc(sizeof(*obj));
  int w=0, h=0;
  IHMemberValue get[] = {
    { IWM_WIDGETRAW_ID, 0 },
    { IWM_WIDTH_ID, 0 },
    { IWM_HEIGHT_ID, 0 },
    { IWM_NULL_ID, 0 }
  };

  if( !obj ) return NULL;

  obj->rep = widget;
  obj->raw = NULL;
  obj->child = NULL;

  get[0].value = &obj->raw;
  get[1].value = &w;
  get[2].value = &h;
  BR_GetWidgetMembers(obj->rep,&get[0]);

  obj->width = w;
  obj->height = h;

  return obj;
}

In order to use its display area, the module defines a function that attaches a low-level X widget to its IHWidgetObj object. Once it has done this, it copies the X widget dimensions into the parent IHWidgetRep object.

void
#ifdef _NO_PROTO
set_widget_child(obj,child)
     IHWidgetObj obj;
     Widget child;
#else
set_widget_child(IHWidgetObj obj,Widget child)
#endif
{
  IHMemberValue set[] = {
    { IWM_WIDGETOBJ_ID, 0 },
    { IWM_WIDTH_ID, 0 },
    { IWM_HEIGHT_ID, 0 },
    { IWM_NULL_ID, 0 }
  };

  if( !obj ) return;

  obj->child = child;

  if( obj->child ) {
    XtVaGetValues(obj->child,
		  XtNwidth,&obj->width,
		  XtNheight,&obj->height,
		  NULL);
  }

  set[0].value = obj;
  set[1].value = (void*)obj->width;
  set[2].value = (void*)obj->height;
  BR_SetWidgetMembers(obj->rep,&set[0]);
}

Finally, a function is defined for handling events that are sent to the module's widget objects. Since most of the user interaction is down through the low-level X widgets, this module doesn't need to worry about many events. The only one it concerns itself with is a resize event; upon receiving one, it changes the child X widget to be the same size as the IHWidgetRep.

int
#ifdef _NO_PROTO
widget_event(evt,obj)
     IHEvent* evt;
     IHWidgetObj obj;
#else
widget_event(IHEvent* evt,IHWidgetObj obj)
#endif
{
  D(bug("Handling widget event $%x for '%s'\n",evt,lm_name));

  /* Sanity checks. */

  if( !evt ) {
    BR_ReportError(NULL,
                   "widget_event() for '%s': IHEvent is NULL.\n",
                   lm_name);
    return 0;
  }

  if( !obj ) {
    BR_ReportError(evt->document,
                   "widget_event() for '%s': IHWidgetObj is NULL.\n",
                   lm_name);
    return 0;
  }

  switch( evt->code ) {
  case IECODE_RESIZE_ID:
    {
      int w, h;
      IHMemberValue get[] = {
	{ IWM_WIDTH_ID, 0 },
	{ IWM_HEIGHT_ID, 0 },
	{ IWM_NULL_ID, 0 }
      };
      get[0].value = &w;
      get[1].value = &h;
      BR_GetWidgetMembers(obj->rep,&get[0]);
      if( obj->child ) {
	XtVaSetValues(obj->child,
		      XtNwidth,obj->width=w,
		      XtNheight,obj->height=h,
		      NULL);
      }
      return 1;
    }
  }

  return 0;
}

IHLangCode Routines

Every embedded applet or document script has a code block associated with it that is defined by the language module. Typically, this code block is the same for both types of contexts, so it is a good idea to factor out the logic for handling them with something like these two functions.

The first function deallocates a IHLangCode object, along with any resources attached to it.

static void free_code(ci,code)
     IHCodeInfo* ci;
     IHLangCode code;
{
  D(bug("Freeing code info $%lx for '%s'\n",ci,lm_name));
  if(code) {
    free(code);
  }
}

The second function allocates a new IHLangCode object. In the process, it parses and saves the parameters associated with that code block. (I.e., the <PARAM> tag and certain attributes of the <OBJECT> tag.) It also uses a document context event allocated by the language environment to compute a default size for its display area.

static IHLangCode
#ifdef _NO_PROTO
alloc_code(ci)
     IHCodeInfo* ci;
#else
alloc_code(IHCodeInfo* ci)
#endif
{
  IHLangCode code = NULL;
  IHEmbeddedParam* param = NULL;

  D(bug("Allocating code info %lx for '%s'\n",ci,lm_name));

  /* Sanity checks. */

  if( !ci ) {
    BR_ReportError(NULL,
                   "alloc_code() for '%s': IHCodeInfo is NULL.\n",
                   lm_name);
    return NULL;
  }

  if( !ci->source ) {
    BR_ReportError(ci->interface->document,
                   "alloc_code() for '%s': No source data.\n",
                   lm_name);
    return NULL;
  }

  if( !ci->langenv ) {
    BR_ReportError(ci->interface->document,
                   "alloc_code() for '%s': IHCodeInfo.langenv is NULL.\n",
		   lm_name);
    return NULL;
  }

  if(code = malloc(sizeof(*code))) {
    IHAttrType type;
    IHAttrVal val=0;

    memset(code,0,sizeof(*code));

    if( (type=BR_GetEventAttr(ci->langenv->query_evt,IEATTR_VIEW_WIDTH_ID,
			      &val)) == ATTRTYPE_INTEGER_ID ) {
      code->width = (int)val;
    }
    BR_FreeEventAttr(ci->langenv->query_evt,IEATTR_VIEW_WIDTH_ID,type,val);

    if( (type=BR_GetEventAttr(ci->langenv->query_evt,IEATTR_VIEW_HEIGHT_ID,
			      &val)) == ATTRTYPE_INTEGER_ID ) {
      code->height = (int)val;
    }
    BR_FreeEventAttr(ci->langenv->query_evt,IEATTR_VIEW_HEIGHT_ID,type,val);

    D(bug("Code '%s' default dimens: %d x %d\n",lm_name,
	  code->width,code->height));

    /* Parse the parameters. */
    while( param = IH_NextNode(&ci->params,&param->node) ) {
      D(bug("Param: %s = %s\n",param->param,param->value));
      if( strcmp(param->param,"__WIDTH__") == 0 ) {
	code->width = atoi(param->value);
      } else if( strcmp(param->param,"__HEIGHT__") == 0 ) {
	code->height = atoi(param->value);
      }
    }

    D(bug("Code '%s' final dimens: %d x %d\n",lm_name,
	  code->width,code->height));
  }

  /* Link in the language code block, and return. */
  ci->code = code;
  return code;
}

Document Scripts

A language module must provide two functions for allocating and deallocating document context scripts. Since this module doesn't implement a document, these functions don't need to do anything. However, here they allocate a code block for the document context, just because its easy to do.

The first function implements the module's FreeModule() functionality, deallocating all language resources attached to a IHModuleInfo structure.

static void
#ifdef _NO_PROTO
LM_FreeModule(mi)
     IHModuleInfo* mi;
#else
LM_FreeModule(IHModuleInfo* mi)
#endif
{
  MARK;
  D(bug("Deallocating module %lx for '%s'\n",mi,lm_name));

  if(mi && mi->code.code) {
    MARK;
    free_code(&mi->code,mi->code.code);
  }
  D(bug("done.\n"));
}

Second is the implementation of the module's AllocModule() functionality, which allocates the code block for the module.

static IHModuleInfo*
#ifdef _NO_PROTO
LM_AllocModule(mi)
     IHModuleInfo* mi;
#else
LM_AllocModule(IHModuleInfo* mi)
#endif
{
  IHLangCode code;

  MARK;
  D(bug("Allocating module %lx for '%s'\n",mi,lm_name));

  /* Sanity checks. */

  if( !mi ) {
    BR_ReportError(NULL,
                   "LM_AllocModule() for '%s': IHModuleInfo is NULL.\n",
		   lm_name);
    return NULL;
  }

  if(code = alloc_code(&mi->code)) {
    return mi;
  }

  return NULL;
}

Embedded Applets

Similar to the document scripts above, functions must be provided for managing any embedded applets in a document. The first function implements the FreeEmbedded() functionality, deallocating the code block associated with an embedded applet.

static void
#ifdef _NO_PROTO
LM_FreeEmbedded(ei)
     IHEmbeddedInfo* ei;
#else
LM_FreeEmbedded(IHEmbeddedInfo* ei)
#endif
{
  MARK;
  D(bug("Deallocating embedded object %lx for '%s'\n",ei,lm_name));

  if(ei && ei->code.code) {
    free_code(&ei->code,ei->code.code);
  }
  D(bug("done.\n"));
}

Also very similar to document scripts, the module implements its embedded applets' AllocEmbedded() functionality by allocating a new code block for the applet.

static IHEmbeddedInfo*
#ifdef _NO_PROTO
LM_AllocEmbedded(ei)
     IHEmbeddedInfo* ei;
#else
LM_AllocEmbedded(IHEmbeddedInfo* ei)
#endif
{
  IHLangCode code;

  MARK;
  D(bug("Allocating embedded object for '%s'.\n",lm_name));

  /* Sanity checks. */

  if( !ei ) {
    BR_ReportError(NULL,
                   "LM_AllocEmbedded() for '%s': IHEmbeddedInfo is NULL.\n",
		   lm_name);
    return NULL;
  }

  if(code = alloc_code(&ei->code)) {
    return ei;
  }

  return NULL;
}

An applet's execution is separate from the execution of its encompassing language environment. The module's ExecEmbedded() function starts the execution of a single applet. In this language module, the function is implemented by allocating a widget object for the applet and creating the X widgets that are used to display its text.

static int
#ifdef _NO_PROTO
LM_ExecEmbedded(ei)
     IHEmbeddedInfo* ei;
#else
LM_ExecEmbedded(IHEmbeddedInfo* ei)
#endif
{
  IHLangCode code;

  MARK;
  D(bug("Executing embedded object for '%s'.\n",lm_name));

  /* Sanity checks. */

  if( !ei ) {
    BR_ReportError(NULL,
                   "LM_ExecEmbedded() for '%s': IHEmbeddedInfo is NULL.\n",
		   lm_name);
    return 0;
  }

  if( !(code=ei->code.code) ) {
    BR_ReportError(ei->code.interface->document,
                   "LM_ExecEmbedded() for '%s': IHLangCode is NULL.\n",
		   lm_name);
    return 0;
  }

  if( !(ei->code.langenv) ) {
    BR_ReportError(mi->code.interface->document,
                   "LM_ExecEmbedded() for '%s': IHLangEnv is NULL.\n",
		   lm_name);
    return 0;
  }

  D(bug("ei=%x, code=%x, langenv=%x\n",ei,code,ei->code.langenv));

  if( ei && (code=ei->code.code) && ei->code.langenv ) {

    if( !(code->obj=alloc_widget(ei->widget)) ) return 0;

    code->viewport = XtVaCreateManagedWidget("viewport", viewportWidgetClass,
					     code->obj->raw,
					     XtNwidth, code->width,
					     XtNheight, code->height,
					     NULL);
  
    code->text = XtVaCreateManagedWidget("text", asciiTextWidgetClass,
					 code->viewport,
					 XtNstring,
					 ( ei->code.source
					   && ei->code.source->buffer )
					 ? ei->code.source->buffer : "<empty>",
					 XtNdisplayCaret, False,
					 NULL);

    widget_set_child(code->obj,code->viewport);

    return 1;  /* Success */
  }

  return 0; /* Failure */
}

Finally, the module's StopEmbedded() function stops the execution of a single applet. When called, this module deallocates the widgets associated with the applet.

static void
#ifdef _NO_PROTO
LM_StopEmbedded(ei)
     IHEmbeddedInfo* ei;
#else
LM_StopEmbedded(IHEmbeddedInfo* ei)
#endif
{
  IHLangCode code;

  MARK;
  D(bug("Stopping embedded object $%x for '%s'.\n",ei,lm_name));

  if( !ei || !(code=ei->code.code) ) return;

  free_widget(code->obj);

  code->obj = NULL;
  code->viewport = NULL;
  code->text = NULL;
}

Language Environment Control

The final part of a language module's interface are those associated with its entire language environment. One IHLangEnv object is associated with each document the module has one or more objects on.

First, the module's FreeLangEnv() function is called after all objects have been removed from a document. It deallocates all of the module resources associated with that document.

static void
#ifdef _NO_PROTO
LM_FreeLangEnv(le)
     IHLangEnv* le;
#else
LM_FreeLangEnv(IHLangEnv* le)
#endif
{
  MARK;
  D(bug("Freeing environment $%lx for '%s'\n",le,lm_name));

  if( le != NULL ) {
    if( le->query_evt ) BR_FreeEvent(le->query_evt);
    le->query_evt = NULL;
  }

  /* The iHTML library takes care of deallocating the actual
     IHLangEnv structure. */
}

When the AllocLangEnv() function is called to create a new language environment, this module allocates an IHEvent object is allocated in the browser's document context. During execution in the environment, queries can be performed on the object for information about the dimensions of the document associated with it.

static IHLangEnv*
#ifdef _NO_PROTO
LM_AllocLangEnv(le)
     IHLangEnv* le;
#else
LM_AllocLangEnv(IHLangEnv* le)
#endif
{
  MARK;
  D(bug("Allocating environment $%lx for '%s'\n",le,lm_name));

  /* Sanity checks. */

  if( !le ) {
    BR_ReportError(NULL,
                   "LM_AllocLangEnv() for '%s': IHLangEnv is NULL.\n",
                   lm_name);
    return NULL;
  }

  /* Fill in custom data for language environment here. */
  if( !(le->query_evt=BR_AllocEvent(le->env.interface,
				    le->env.interface->document,
				    le,IECLASS_DOCUMENT_ID,
				    IECODE_SCROLL_ID,NULL)) ) {
    return NULL;
  }

  return le;
}

A module's ExecLangEnv() function is called when a document is being displayed and needs to start executing the scripts associated with it.

static int
#ifdef _NO_PROTO
LM_ExecLangEnv(le,main)
     IHLangEnv* le;
     IHModuleInfo* main;
#else
LM_ExecLangEnv(IHLangEnv* le,IHModuleInfo* main)
#endif
{
  IHLangCode code;

  MARK;
  D(bug("Executing language environment $%x for '%s'.\n",le,lm_name));

  /* Sanity checks. */

  if( !le ) {
    BR_ReportError(NULL,
                   "LM_ExecLangEnv() for '%s': IHLangEnv is NULL.\n",
		   lm_name);
    return 0;
  }

  /* Check if this environment has a main (document) program; if so,
     start it up. */

  if( main && (code=main->code.code) ) {

    D(bug("Starting document program...\n"));
      
  }

  return 1;
}

Finally, the module's StopLangEnv() function terminates document context execution within a language module.

static void
#ifdef _NO_PROTO
LM_StopLangEnv(le,main)
     IHLangEnv* le;
     IHModuleInfo* main;
#else
LM_StopLangEnv(IHLangEnv* le,IHModuleInfo* main)
#endif
{
  IHLangCode code;

  MARK;
  D(bug("Stopping language environment $%x for '%s'.\n",le,lm_name));

  if( !le ) return;

  if( main && (code=main->code.code) ) {

    D(bug("Stopping document program...\n"));

  }
}

[Home] [ToC] [Up] [Prev] [Next]

_________.oo_Q_Q_oo.____________________________________________
Dianne Kyra Hackborn <hackbod@angryredplanet.com>
Last modified: Sat Oct 26 14:58:06 PDT 1996

This web page and all material contained herein is Copyright (c) 1997 Dianne Hackborn, unless otherwise noted. All rights reserved.