You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



You are here: Editor project page > Writing Your Own Ender HTML Typing Rules

Writing Your Own Ender HTML Typing Rules

jfrancis@netscape.com

A brief tour into how to create and use a different rules system in Ender, such as it is.

First, note that the rules system architecture is not mature. There is just enough there to encourage us, as developers, to separate out the rules oriented code well enough so that we don't get hosed in the future when we want alternate rules systems.

The rules system interface is not XPCOMified at the moment. We may do that in the future. In the meantime, one of the consequences is that all rules code needs to be linked against the editor. The easiest way to do that is just to include the rules as part of the editor dll itself. So rules aren't plugable right now, though again, that could be changed.

There are two rules systems, one for the text-only editor and one for the html editor. The latter is oriented towards mail compose at the moment. The implementation for these rules systems are in nsTextEditRules.cpp and nsHTMLEditRules.cpp, respectively.

Every instantiation of an editor has it's own rules object. nsTextEditor and nsHTMLEditor both have an InitRules() function which handles this. Until we make rules plugin at the runtime level, you will have to settle for plugging them in programmatically by altering the InitRules() method of nsHTMLEditor to use your rules system instead.

For every call into the editor that might want different behavior depending on your rules, there is a corresponding "code sandwich". The routine will call

mRules->WillDoAction(..., *aCancel);

If aCancel is true, the routine will not perform the action itself, it was handled by the rules system. Otherwise, the routine will perform the default version of the action, and then will call:

mRules->DidDoAction(...)

giving the rules system a chance to do any post processing it wants.

Take a look at nsHTMLEditorRules.cpp to see the signature for WillDoAction()/DidDoAction(), and how they might be used.

There is an extra informational structure passed throughout the code sandwich which is a complete hack at the moment - just a glom of whatever is needed by any of the rules. A better way of bundling extra info for the benefit of rules should be written - something that will allow arbitrary tuples of {name of info, the info} to be passed through.

In order to understand much of the rules code, you will have to understand several building block technologies that are in the editor or layout. These include:

  • The DOM and the Content model (nsIDOMNode and nsIContent)
  • Ranges (nsIDOMRange)
  • Selection (nsIDOMSelection)
  • Content Iterators (nsIContentIterator)
  • Editor helper functionality (in nsEditor.cpp at the moment)
  • The Transaction manager

I'll go over some of these very briefly.

The DOM and the Content model (nsIDOMNode and nsIContent)

For info on these see: http://www.mozilla.org/newlayout/dom-roadmap.html and http://www.mozilla.org/newlayout/doc/contentmodel.html.

Ranges (nsIDOMRange)

Ranges are pretty straightforward, and are a way of specifying an arbitrary span of a DOM tree. A range has a startpoint and an endpoint, which are both tuples of {parent, offset}. The only tricky parts are:

* in start/endpoints that are inside text nodes the offset refers to where in the actual character data the range start/end is, while in non-textnodes the offset specifies between which child nodes of the parent the range starts/ends.

* ranges move around under content manipulation. How this happens is all spelled out in the range specification at:
http://www.w3.org/TR/WD-DOM-Level-2/range.html
It's important to understand this "DOM gravity" aspect of ranges, because selection is implemented in terms of ranges, and you will often need to understand how content modifications may affect the selection in order to properly deal with this in editor transactions.

Selection (nsIDOMSelection)

We don't have a good selection doc that I know of right now. Mike has been doomed to writing and rewriting selection and has done a great job with it.

Maybe he can chip in here. Take a look at nsIDOMSelection and remember that selection objects are made up of a list of ranges - ie, you might have several non-overlapping ranges making up one selection. I know of no way to actually get more than one range right now, but you should code as if there are a list of them.

Content Iterators (nsIContentIterator)

We don't have any kind of content iterator doc right now.

The basic idea with the iterators is to try to put into one place the code needed to navigate, left to right or right to left, over an arbitrary range in the content tree. At the moment the iterators default to post-order, or what I call "close tag order". Ie, you get parents after children, and you only get parents if the range includes the "end" of the parent, ie if it included the close tag, if you think of it in terms of the html stream.

There are two types of iterators, the content iterator and the subtree iterator.

The content iterator does what you'd expect and will iterate over every node in the range. They are used, directly or indirectly, by much of the rules code.

The subtree iterator iterator is like the content iterator, but in any situation where the content iterator would have returned both a node and that nodes parent somewhere along the way, the subtree iterator will only return the parent. The original inspiration for the subtree iterator was to do efficient deletion over a range: if you remove the parent of some node you obviously didn't need to remove it's children. It turns out this kind of iterator is useful in selection as well. Perhaps it will be useful in some rules code someday.

Editor helper functionality (in nsEditor.cpp at the moment)

There is a bunch of utility code already written for doing many of the kinds of things frequently needed when writing rules code. Check out nsEditor.cpp and also nsHTMLEditor.cpp for a host of helper routines. There is a document floating around (get Steve to put it up on Mozilla) on Block Transformation functions. In the future I hope to document the various utility functions, but for now the source and comments are the documentation. There is a lot of duplication in the utility functions that is slowly getting cleaned up.

The Transaction Manager

There is a good doc on Transaction Manager / Undo System. Note the use in the editor of BeginTransaction() / EndTransaction(), which are used to make sure a series of actions gets grouped together for undo purposes, and also disables some drawing while a transaction is in progress.