Table of Contents

Integration of HTMLayout with a scripting engine

Let's say we need to integrate HTMLayout with some scripting engine in the same way as JavaScript is integrated with html DOM in conventional browsers. This means that:

      var root = window.document; 
      var some = root.getElementById( "some" );
      ...
 

      var some = root.getElementById( "some" );
      some.onMouseDown = function() { .... } 
      ...
 

Basic principles

Each DOM element that script is interested in should have some script object associated with it. Such an object is usually named as expando. That association can be made by using event_handler structure. Each DOM element here may have zero or more event handlers associated with it. One of them will be our script_expando - event handler and holder of the expando object reference.

struct script_expando

Here is rough schema of how I understand script_expando should be done:

 
struct script_expando: public event_handler 
{
  SCRIPT_OBJECT_HANDLE expando; // script object that represents this DOM element.
 
  script_expando():event_handler(ALL_EVENTS)
 
  virtual BOOL on_method_call (HELEMENT he, UINT methodID, METHOD_PARAMS* params ) 
  {
    if( methodID == GET_EXPANDO ) // someone requesting expando object reference for the he element.
    {
       GET_EXPANDO_OBJECT_PARAMS *pp =  (GET_EXPANDO_OBJECT_PARAMS *) params;
       pp->ex = this; // return it.
       return TRUE;
    }
    return FALSE;
  }
 
  virtual BOOL on_mouse(...)
  {
    call_script_method(expando, "onMouseDown",...); // call script method onMouseDown
  }
 
  virtual void detached  (HELEMENT /*he*/ ) 
  { 
    // detached event is a sort of finalizer/destructor event - element is going out of the DOM
    call_script_method(expando, "detached",...); // call script method detached
    delete this; // destroy this structure.
  } 
  virtual void attached  (HELEMENT /*he*/ ) 
  { 
    // 'attached' here is very close to the constructor method
    call_script_method(expando, "attached",...); // call script method attached
  } 
};

function get_script_object_of(element)

Another thing that we need is a function that will return expando object of given DOM element. This function uses HTMLayoutCallBehaviorMethod API to call our custom method. That method is identified by GET_EXPANDO_OBJECT constant and uses GET_EXPANDO_OBJECT_PARAMS as parameters of the method.

struct GET_EXPANDO_OBJECT_PARAMS : METHOD_PARAMS 
{
  script_expando* ex;
};
 
static SCRIPT_OBJECT_HANDLE null_handle = NULL;
 
SCRIPT_OBJECT_HANDLE& get_script_object_of( HELEMENT he, bool create_if_needed )
{
  GET_EXPANDO_OBJECT_PARAMS p; 
  p.methodID = GET_EXPANDO_OBJECT;
  p.ex = 0;
  if(::HTMLayoutCallBehaviorMethod(he,&p)) // this will end up in on_method_call (HELEMENT he ...) above
  { // object has script_expando struct associated with it already so return expando from it...
    return p.ex->expando;
  }
  // object has no script_expando object yet so ... 
  if( !create_if_needed )
     return null_handle; 
 
  script_expando* pexp = new script_expando();
 
  // let's create new expando script object.
  // Just for example I use various classes for different types of controls
  // but you may choose some other schema:  
 
  int ctltype = HTMLayoutControlGetType(he);
  const char* class_name = "Element";
  if( ctltype == CTL_EDIT )
     class_name = "Edit";
  else if( ctltype == CTL_BUTTON )
     class_name = "Button";
  else 
     ...
 
  pexp->expando = create_script_object_of_class( class_name );
  associate_foreign_pointer(pexp->expando, he); // store he ref inside scripting object 
                                                // for use in DOM element related methods
  ::HTMLayoutUseElement(he); // increase ref counter as we are storing he by associate_foreign_pointer and see note below.
 
  // attach it to the DOM element:
  HTMLayoutAttachEventHandler(he, script_expando::element_proc, pexp);
 
  return pexp->expando; 
}

Done. We have implemented script object association with DOM element and we are able to catch events on DOM elements and pass them to script methods.

Garbage in, garbage out

Last thing that we need to do here is GC handling. Each script engine deals differently with GC so here is generic schema for copying GC:

void do_GC( HELEMENT root)
{
  foreach_element_in_the_dom_under_the_root do
  {
    SCRIPT_OBJECT_HANDLE& handle = get_script_object_of(he, false);
    if(&handle != &null_handle)
      handle = script_copy_object(handle); // copy object to the second half of the heap.
  }
}

So we are scanning all elements in the DOM tree and for those that have expando objects associated we are letting know the heap manager that objects that are currently in the DOM are alive so need to be preserved.

For all objects that were not copied (so they are garbage that was collected) you need to call ::HTMLayoutUnUseElement(he); to free references of DOM elements. In .NET that can be made in finalize() method for example. See HTMLayoutUseElement above.

Done.