This document describes the current state of editor embeddability, problems with the existing implementation, some possible embedding scenarios that we need to deal with, and an embedding solution that will fulfill them. Finally, steps towards that final solution are described.
Here are some embedding scenarios that editor needs to deal with. Note here that I use the term 'Composer' to mean an HTML-savvy compose widget that does rich text editing, and 'Editor' to mean a plain text editor (as well as the underlying technology for Composer). <htmlarea> is intended as a shorthand for a rich-text multiline text widget embedded in an HTML document, and is not meant to infer that future versions of Mozilla will support this specific tag.
Developers need to embed composer widgets in their XUL applications, by using the <editor> tag as we do today. They should need to do as little work as possible to get basic editing functionality, be able to have any number of <editor>s per window, and control whether those <editor>s are in HTML or plain text mode.
In this application, the <iframe> on which the editor lives is embedded directly in the native application; this is equivalent to embedding the browser via nsIWebBrowser, but instead having an editable document. The composer chrome (toolbars etc) may be implemented by the embedder using native widgets, or using some amount of XUL. That chrome needs to be configurable - dockable floating toolbars, toolbar shared between composer widgets, or 1 per widget.
This type of embedding requires that the composer code is agnostic to where its UI is coming from; communication between the core editor and the UI needs to go through one or more interfaces that insulate the editor from its host application. (The current nsEditorShell makes assumptions about the hosting XUL document, which need to be broken.)
<htmlarea>) IE 5 supports the <HTMLArea> element; if Mozilla is to support something similar, editor needs to be embeddable to the extent that this can be done. It is likely that we'd use XBL to implement this type of widget, as is the plan for other form controls.
As is the case for composer embedded in a native application, there is a requirement here that the composer UI be configurable, such that it can be displayed either as a toolbar at the top of the <htmlarea>, a floating palette, or a top-level toolbar.
The current composer architecture was created while other parts of Mozilla were still under development, and as a result it suffers from a number of shortcomings, and anachronisms. This section describes its major faults.
The editor in a composer window is current owned by the nsEditorShell, which in turn is created, owned and destroyed by the nsEditorBoxObject. The box object is a layout structure that is owned by content nodes, and survives frame destruction/recreation. The box object also has a reference to the docShell for the editor frame. XBL creates an nsEditorBoxObject for each <editor> tag, and allows JavaScript to access properties of this box object (such as the nsIEditorShell). The <editor> tag is simply a <iframe> on which an editor is created; in all other respects, it behaves like a XUL <iframe>.
The problem with this ownership model is that there can be only one editor per <editor> tag, yet the document loaded in the <iframe> may itself contain multiple <iframe>s (consider a frameset document, or a document itself containing an <html:iframe>). Currently, composer does not deal gracefully with such documents.
The current composer window XUL/C++ architecture has grown up with the assumption that there can be just one <editor> tag per window. At window construction time, we get the editorShell from the <editor> element, and put that into window.editorShell. Thence, lots of JavaScript in editor.js, ComposerCommands.js and the various dialog JS files assume that they can get at the one true editor via window.editorShell. This assumption was short-sighted, and must be fixed.
There is C++ and JS code in the editor that assumes that the editor is living in a XUL document, and that there are XUL document nodes out there whose attributes can be tweaked to change the state of the UI (e.g. the inline style buttons). This needs to be changed to allow embedders to provide their own, possibly native UI. Editor needs to call through one or more interfaces when communicating with the UI.
Editor requires design changes such that the embedding applications above can be fulfilled, and these changes will need to address the current problems. Briefly, the embedding objectives are:
<editor> should get you a working editor in a XUL application
<editor>s per XUL window
Meeting these objectives will also solve the following existing composer problems:
<iframe>s.
As described above, editor rooting needs to be changed so that an editor lives on top of an nsDocShell, rather than hanging off the nsEditorBoxObject. There is a docShell for each editable <iframe>. This will involve:
nsIEditorFrame, that is implemented by nsDocShell or a related class. You should be able to QI from nsIDocShell to one of these; if this succeeds, it indicates that the frame is editable. nsIEditorFrame should contain methods for getting the editing session, and doing some generic editor-related stuff (probably common to HTML and plain text editing). A subset of nsIEditorShell will probably move into this interface. (This would be analogous to the nsIWebNavigation interface used for a browser.)
<iframe> in composer, will instantiate more than one low-level editor. We need a concept of an "editing session" - a single top-level document which is editable, and which may embody more than one editor. This interface will be called nsI????. High-level UI and emebedding code should deal with this editing session interface, without knowledge of the underlying editors. The editing session will forward commands to the individual editors depending on focus, and mediate undo/redo between them.
Current Composer clients in the Mozilla codebase all assume that there is only one <editor> tag per window. They all need to be able to deal with multiple editors. Fixing this would require JS changes of the following kind:
window.editorShell, then this needs to be updated on focus changes.
window.tryToClose) appropriately consult each editor for state.
Composer needs to not know anything about the UI that is driving it. The plan is to insulate Composer from the UI via a new interface that the embedder implements. Any UI that is now thrown up by Composer should go through this interface.
nsIEditorUserInterface, to mediate communication between the editor and the UI. An embedder would need to implement this to get native toolbars and menus to work. In Composer, we'd have an implementation in JS that talks to the existing commands, and updates the XUL nodes.
nsIEditorUserInterface.
This section attempts to lay out an implementation plan, with the aim of keeping everything working as the various steps are taken. Some of these tasks can be done concurrently.
nsIEditorUserInterface
nsIEditorFrame
It seems that some embedders will want composer Open and Save file logic, and some won't. Where should this logic live? Can it be in JavaScript? Of course, an embedder should be able to use its own Open and Save dialogs, and communicate with composer to coordinate the open or save process.
There is a huge amount of Composer UI in the various dialogs for editing tables, links, images etc. Do we need to make it possible for an embedder to replace all of these with native UI?
Dialogs use the available editor APIs to get and set data, so can do all their work through existing APIs. If an embedder wants a fully native UI, then they'll have to code their own dialogs, and associated logic, but the APIs should already be available to them. This seems to be a non-issue.
Page last modified 18:14, 16 Mar 2007 by Mgjbot