Replacing the DHTML Editing Control in Windows Vista and Beyond

 

Van Kichline
Microsoft Corporation

August 2006

Summary: The DHTML Editing Control will not be included in Windows Vista. Web pages and Windows applications that use the control must be modified to continue to provide a WYSIWYG HTML Editor. (25 printed pages)

Applies to:
   Internet Explorer 7 on Windows Vista
   Web pages intended for Internet Explorer that host the DHTML Editing Control
   Windows applications that use the DHTML Editing ActiveX control or the TriEdit ActiveX document

Contents

Introduction
Effect on Windows Applications
Effect on Web Applications
Summary

Introduction

The DHTML Editing Control is an ActiveX control designed for WYSIWYG HTML editing in Web pages and Windows applications. It was originally released to the ActiveX Control Gallery in early 1998 by the Microsoft Visual InterDev development team. It quickly became apparent that the tight coupling between Internet Explorer and the control made it desirable to ship the control as part of each Internet Explorer update, and the control has been shipped as part of Internet Explorer since the release of Internet Explorer 5 in early 1999.

Several factors combined to make continued support of the control less desirable over time. Internet Explorer has continued to evolve, incorporating WYSIWYG editing features of its own in Internet Explorer 5.5 and Internet Explorer 6. At the same time, security has become a major focus for Internet Explorer. To increase security, the browser restricts access to potentially dangerous content, but the editor focuses on ensuring that its changing content is safe to persist. Several security bulletins resulted in a more locked-down browser environment, which reduced the utility of the DHTML Editing Control and raised its cost of ownership significantly.

The decision was therefore made to remove the control from Windows Vista. The control was present in the Beta 1 version of Windows Vista, but is absent in subsequent Beta releases and in the final release of Windows Vista.

This paper describes the effect of this decision on existing applications that use the control, how to determine what applications and Web pages use the control, and how to mitigate the impact of this change on these applications and on Web pages.

Anatomy Lesson

The DHTML Editing Control supports "WYSIWYG HTML editing," which means that the user can type text into the control while seeing formatted HTML presented as it would be on a Web page. Various commands let the user adjust fonts, colors, and formatting and see the effects immediately. This is similar to using a rich text box control in a client form, but the data saved by the control is HTML instead of RTF.

The DHTML Editing Control is more complex than many other controls. It consists of two components: Dhtmled.ocx and TriEdit.dll. The components are in the following files, which have been removed for Windows Vista:

  • [drive:]\Program Files\Common Files\Microsoft Shared\Triedit\dhtmled.ocx
  • [drive:]\Program Files\Common Files\Microsoft Shared\Triedit\TRIEDIT.DLL

The two components in turn implement five distinct objects:

Dhtmled.ocx

  • Safe-for-scripting DHTML Editing Control (DHTMLSafe)
  • DHTML Editing Control for Applications (DHTMLEdit)
  • DEGetBlockFmtNamesParam helper object
  • DEInsertTableParam helper object

TriEdit.dll

  • TriEdit Document object

Only the safe-for-scripting DHTML Editing Control, the DEGetBlockFmtNamesParam object, and the DEInsertTableParam object are marked safe for scripting and can be created on a Web page. DEGetBlockFmtNamesParam and DEInsertTableParam are simple objects that are used exclusively to pass data to and from some commands implemented by the DHTML Editing Control.

The DHTML Editing Control for Applications is a more powerful version of the control that aggregates the safe-for-scripting control and is intended to be used in ActiveX hosting applications, such as those that have been created by using Visual Basic or Delphi. This control has also been excluded from Windows Vista. However, Microsoft licenses a work-alike version of the control that can be installed by third parties to enable applications relying on this control to continue working on Microsoft Vista. For more information, see the section Mitigation Strategies for Windows Applications later in this document.

The TriEdit Document object is an ActiveX Document that is instantiated with CoCreate by both versions of the DHTML Editing Control, and that can be separately instantiated by ActiveX Document hosting applications. ActiveX Documents can be significantly more complex to host than controls, but they enable more direct control over the appearance and integration of the editing surface. As with the DHTML Editing Control for Applications, the TriEdit Document object will be supported in Windows Vista with a third-party work-alike object.

In addition to providing a WYSIWYG HTML editing surface and a set of properties, methods, and events oriented to typical editing tasks, both the DHTML Editing Control and TriEdit Document object support these additional features:

  • Multi-level undo and redo.
  • Rudimentary table editing.
  • Selection features such as drag-move, resizing, and UI Activation.
  • Absolute positioning and Z-ordering.
  • Source code preservation.
  • HTML tag glyph rendering.
  • Design-time control (DTC) support. (This was a feature available in Microsoft Visual InterDev.)
  • Keyboard accelerators.

Some of these features have been incorporated into Internet Explorer through a feature referred to as MSHTML editing, as implemented by using the DHTML designMode property. However, not all DHTML Editing Control features are supported in MSHTML editing. For example, source code preservation in the DHTML Editing Control enables it to retain HTML source code formatting; source-code preservation is not available in MSHTML editing. Absolute positioning and Z-ordering are now handled by Internet Explorer. Table editing is not supported, nor are DTCs.

Web pages that use the DHTML Editing Control specifically because of its source code preservation capability will be severely affected by this change. There is no convenient alternative available for this functionality, and it is a challenging technical problem to implement a replacement.

Effect on Windows Applications

Windows applications that incorporate the DHTML Editing Control for Applications will no longer function as intended on Windows Vista. Visual Basic applications, for example, might display the following message when they are opened, or when the form that contains the control is instantiated:

Component 'dhtmled.ocx' or one of its dependencies not registered correctly: a file is missing or invalid.

Delphi applications might throw unhandled exceptions.

Determining What Windows Applications Are Affected

You can determine whether Visual Basic applications contain the DHTML Editing Control with the Windows findstr command, by using syntax such as the following:

findstr /m DHTMLEDLibCtl.DHTMLEdit YourApplication.exe

Delphi applications contain the strings "DHTMLEDLib_TLB" and "IDHTMLEdit". Therefore, you can use the following command to find both Visual Basic and Delphi applications that use the control:

findstr /im dhtmledlib YourApplication.exe

C and C++ applications might use TriEdit directly instead of the using the DHTML Editing Control. Instead of loading a type library, low-level languages frequently use the CoCreateInstance method to instantiate an OLE object. In that case, the GUID used to create the object will be compiled into the application. The GUIDs for the control and TriEdit document are as follows:

  • TriEdit Document: {438DA5E0-F171-11D0-984E-0000F80270F8}
  • DHTMLEdit: {2D360200-FFF5-11d1-8D03-00A0C959BC0A}
  • ITriEditDocument: {438DA5DF-F171-11D0-984E-0000F80270F8}
  • IDHTMLEdit: {CE04B591-2B1F-11D2-8D1E-00A0C959BC0A}

If you must search compiled code, you can look certain patterns that represent the GUIDs. For example, the following GUID:

{ABCDEFGH-IJKL-MNOP-QRST-UVWXYZ012345}

becomes the following hexadecimal sequence in binary:

GH EF CD AB KL IJ OP MN QR ST UV WX YZ 01 23 45

The following example shows a simple C# program to search executable binaries for any of these GUIDs.

// Compile and execute:  "FindGUIDs YourApplication.exe"
using System;
using System.Text;
using System.IO;

namespace FindGUIDs {
class Program {
  static void Main(string[] args) {
    FileStream    fs = File.OpenRead(args[0]);
    StringBuilder sb = new StringBuilder();
    do {
      Int32 b = fs.ReadByte();
      if (-1 == b) {
        break;
      }
      sb.AppendFormat("{0:X2}", b);
    } while (true);
    fs.Close();
    String s = sb.ToString();
    if (s.Contains("E0A58D4371F1D011984E0000F80270F8"))
      Console.Out.WriteLine("GUID for TriEditDocument Class detected.");
    if (s.Contains("DFA58D4371F1D011984E0000F80270F8")) {
      Console.Out.WriteLine(
        "GUID for ITriEditDocument Interface detected.");
      }
      if (s.Contains("0002362DF5FFd1118D0300A0C959BC0A")) {
        Console.Out.WriteLine("GUID for DHTMLEdit Class detected.");
      }
      if (s.Contains("91B504CE1F2Bd2118D1E00A0C959BC0A")) {
        Console.Out.WriteLine("GUID for IDHTMLEdit Interface detected.");
      }
    }
  }
}

Because C, C++, and other low-level languages are free to instantiate COM objects in several ways, the method illustrated in the sample is not guaranteed to locate all instances of the control or document object. If this test is negative, use the Windows findstr command to search for text representations of GUID fragments, such as 438DA5E0 or 2D360200. If source code is available, inspect it carefully. Search for GUID fragments or text such as "dhtmledit" or "triedit". It is important to search for fragments of GUIDs because there are several ways that GUIDs can be represented as text. The first eight digits are consecutive and are generally unique, therefore serving as a good search pattern.

Mitigation Strategies for Windows Applications

Although the DHTML Editing Control and TriEdit are being removed from Windows Vista, Microsoft will make both binary components available to ISVs as a component packaged in a signed Microsoft Windows installer (.msi file). The safe-for-scripting version of the DHTML Editing Control will not be included in this package. Licensing information is included in the End User License Agreement displayed by this installer. Please review this carefully before installing or redistributing the package.

To obtain the Windows installer, visit the Microsoft Download Center Web site. This installer is signed and contains signed binaries for the control and for TriEdit, both modified for use on Windows Vista. This package will install only on Windows Vista. It will not run on earlier versions of Windows such as Windows XP. If you try to run the installer on an earlier version of Windows, a dialog box will display the error:

This installer is designed for Windows Vista and installation will stop.

To install the file, you can double-click it, you can right-click it and select Install, or you can run the following command:

msiexec /i dhtmled.msi

This will install and register Dhtmled.ocx and Triedit.dll into the following directory:

%windir%\Program Files\Common Files\Microsoft Shared\dhtmled\

Installing the control will add a "DHTML Editing Component" entry to the Add or Remove Programs application in the Windows Control Panel.

After the Windows Vista versions of the binary components are installed, the user experience with the DHTML Editing Control for Application and TriEdit will be indistinguishable from the user experience on earlier versions of Windows. All functionality is preserved, and all GUIDs, ProgIDs, type library names, and so on are identical. (There is a difference in the user experience for the safe-for-scripting DHTML Editing Control, which is omitted on Windows Vista. For more information, see the section Effect on Web Applications later in this document.)

Legacy Applications

If you use the Dhtmled.msi installer, your legacy applications will work under Windows Vista as they always have. If you have not installed the new binary components, legacy applications that include the DHTML Editing Control for Applications will exhibit various failure modes when they are executed under Windows Vista. If you are an application vendor, you can provide an update or send directions for obtaining the Windows Vista installer directly to end users, and users can then install the Windows Vista versions themselves and continue to use their applications. It is likely that users who experience errors after they install Windows Vista will search the Web for the text of error messages and for the name of your product before contacting your customer service. Therefore, you should post information about locating the installer. Downloading and installing the Windows Vista versions of the binary components will be straightforward for most users.

New Applications

For new applications, you can ship the installer as part of your product (with appropriate licensing) and chain to it from your own installer. The installer can be run silently and without adding an item to the installed-programs list in Windows. (For details, see the documentation for MsiExec.exe.)

However, we recommend that the installer allow the DHTML Editing Control to be added to the installed-programs list. In addition, when your application is uninstalled, do not uninstall the control; instead, allow customers to uninstall it separately if they want.

Note that this method of installation does not provide logo-compliant installation. It was determined that this mitigation was acceptable for the following reasons:

  • A limited number of applications include the components.
  • The chance of a single computer hosting several of these applications is low.
  • If an application is broken by another application uninstalling the DHTML Editing Control, the broken application can easily be repaired using the Repair option available in the Add or Remove Programs application.

If your application requires logo compliance, you will have to instruct the user to install the DHTML Editing Control for Windows Vista separately.

Effect on Web Applications

The DHTML Editing Control has been available for HTML, ASP, and ASP.NET pages, and for other documents accessed with HTTP, since Internet Explorer 5 was released. On Windows Vista, Web pages that use the safe-for-scripting DHTML Editing Control will fail to load the control. Instead, an image placeholder will be displayed:

Aa663363.htmleditinfuture01(en-us,MSDN.10).gif

In addition, any script that references the control will throw exceptions. Because script exceptions terminate script evaluation, it is likely that unrelated functionality controlled by script will also be rendered inoperative. Therefore, you must modify or retarget pages that contain the safe-for-scripting DHTML Editing Control and that will be available to Windows Vista clients.

Note   Originally, the DHTML Editing Control for Applications (the one that is not safe for scripting) could be used on a Web page also (with a warning message), but with the release of Internet Explorer 6, security hardening within the control made the application version virtually unusable on a Web page. It is unlikely that any functional Web pages instantiate the DHTML Editing Control for Applications.

Determining What Web Pages Are Affected

Web pages that contain the safe-for-scripting DHTML Editing Control will implement it as an <object> tag such as the following:

<!-- The DHTML Editing Control; original GUID -->
<OBJECT id="editor" height=400 width=640
  classid="clsid:2D360201-FFF5-11d1-8D03-00A0C959BC0A">
</OBJECT>

Many Web pages using the control were created with Microsoft Visual InterDev, or they used example code generated by Visual InterDev. In that case, the markup will resemble the following:

<OBJECT classid="clsid:2D360201-FFF5-11D1-8D03-00A0C959BC0A"
    class="anyClass" ID="editor" VIEWASTEXT>
  <PARAM name="ActivateApplets" value="0">
  <PARAM name="ActivateActiveXControls" value="-1">
  <PARAM name="ActivateDTCs" value="-1">
  <PARAM name="ShowDetails" value="0">
  <PARAM name="ShowBorders" value="0">
  <PARAM name="Appearance" value="0">
  <PARAM name="Scrollbars" value="-1">
  <PARAM name="ScrollbarAppearance" value="1">
  <PARAM name="SourceCodePreservation" value="-1">
  <PARAM name="AbsoluteDropMode" value="0">
  <PARAM name="SnapToGrid" value="0">
  <PARAM name="SnapToGridX" value="50">
  <PARAM name="SnapToGridY" value="50">
  <PARAM name="UseDivOnCarriageReturn" value="1">
</OBJECT>

The additional tags represent initialization values for parameters supported by the control. Web page design applications that persist a control's property bag will include these PARAM tags with the HTML content when the page is saved.

The most effective quick check is to search your store of Web pages for the common GUID (2D360201-FFF5-11D1-8D03-00A0C959BC0A) or just the fragment 2D360201. Pages that contain this text almost certainly include the safe-for-scripting DHTML Editing Control.

Mitigation Strategies for Web Applications

In response to the DHTML Editing Control and TriEdit being removed from Windows Vista, Microsoft has made the DHTML Editing Control for Applications and TriEdit available as a system component packaged in a signed Windows installer (.msi file). However, the safe-for-scripting version of the DHTML Editing Control will not be included in this package.

Much of the functionality that the DHTML Editing Control implements has been made available directly in Internet Explorer through MSHTML editing—that is, through use of the designMode property. Therefore, in many cases the functionality of the control can be replaced with MSHTML editing—for example, with an <iframe> element placed into design mode, as explained later in this document.

Using Alternative Controls

One mitigation strategy is to use alternative ActiveX controls or ASP.NET custom controls as replacements. Searching the Web for "HTML WYSIWYG Editor Control" suggests several candidates. Microsoft has not conducted a survey of these products and cannot make recommendations. Therefore, you must determine the best fit for your needs. Any replacement is likely to have a different object model from the DHTML Editing Control and will require modification of supporting script on the page using the control. The difficulty of this modification depends on the degree of sophistication of the implementation.

Implementing Selective Redirection

Only Internet Explorer browsers running on Windows can instantiate the safe-for-scripting DHTML Editing control. Therefore, if you have a "reach" application intended for various browsers, you might already have browser detection and redirection built into the application. For example, the application might redirect Internet Explorer requests to a page that contains the control, and redirect other browser requests to a page that uses an alternative means of input.

You can determine whether a request originates with Windows Vista by examining the user agent string sent with the request. The user agent string for Internet Explorer 7 on Windows Vista will resemble the following:

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; MSDigitalLocker Vista 
1.3; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; WinFX RunTime 
3.0.50727; InfoPath.1)

The following example in C# shows code for detecting and redirecting a request from Windows Vista and Internet Explorer 7.

protected void Page_Load(object sender, EventArgs e) {
    string ua = Request.UserAgent.ToLower();
    bool isVista = ua.Contains("windows nt 6.");
    bool isIE7 = ua.Contains("msie 7.");
    Response.Redirect((isVista && isIE7) ?
        "Surrogate.htm" : "DHTMLEd.htm");
}

In Visual Basic Scripting Edition (VBScript) for ASP, the following code accomplishes the same task.

<% @Language = "VBScript" %>
<%
Dim ua, isVista, isIE7
ua = LCase(Request.ServerVariables("HTTP_USER_AGENT"))
isVista = (0 < InStr(ua, "windows nt 6."))
isIE7   =  (0 < InStr(ua, "msie 7."))
If isVista AND isIE7 Then
    Response.Redirect "Surrogate.htm"
Else
    Response.Redirect "DHTMLEd.htm"
End If
%>

Reimplementing Editing Functionality

If the capabilities of the DHTML Editing Control are absolutely necessary and substitution is impractical, the best strategy might be to reimplement the control's capabilities using MSHTML editing and script. MSHTML editing is available in Internet Explorer 5.5 and later versions; the DHTML Editing Control can be used with Internet Explorer 5.0 and later versions. Therefore, if you reimplement the functionality, you might still need to provide a redirected page that contains the DHTML Editing Control for users who have Internet Explorer 5.0.

This section shows one way to reimplement the capabilities of the safe-for-scripting DHTML Editing Control by using an <iframe> element. You can implement basic editing capabilities using a page such as the following:

<html>
<head>
<title>DHTML Editing Control Surrogate Example</title>
<script type="text/javascript">
function FillEditor() {
    var doc = editor.document;
    doc.designMode = "on";
    doc.write("<body><p><i>Visualize</i> <u>peace</u></p></body>");
    doc.close();
}
</script>
</head>
<body onload="FillEditor()">
  <iframe id="editor" scrolling="yes" height="100" width="300">
  </iframe>
  <p>
  <input type=button 
     value="Submit" 
     onclick=
       "alert(editor.document.getElementsByTagName('HTML')[0].outerHTML)">
</body>
</html>

This page displays an editable area that contains formatted HTML. You can type in this area and use hot keys such as CTRL+I to toggle italics and CTRL+U to toggle underline. When you press the Submit button, a message box displays the HTML content of the <iframe> element. To implement the equivalent of the DHTML Editing Control's properties, methods, and events, you must use script.

For security reasons, only content from the same domain can be accessed across the <iframe> boundary. To make sure that the content of the frame can be accessed, initialize it from a URL in the same domain by using the src attribute, as in the following example:

<iframe id="editor" 
  scrolling="yes" 
  height="100" 
  width="300" 
  src="templates/blank.htm">
</iframe>

For more information about editing using the designMode property in Internet Explorer, see the page Introduction to MSHTML Editing on the Microsoft Web site.

Methods

The following table lists methods from the safe-for-scripting DHTML Editing Control and whether they can be reimplemented using script in Internet Explorer.

Methods of the safe-for-scripting DHTML Editing Control

Command Supportable?
ExecCommand Partially
QueryStatus Partially
SetContextMenu Yes
NewDocument Yes
LoadURL Yes
FilterSourceCode No
Refresh Not required

The ExecCommand and QueryStatus methods can be replaced by calling editor.document.execCommand and query commands on the editor.document object. A subset of the commands implemented by the DHTML Editing Control is available through scripting, as described in the section CommandID Values later in this document.

To get the functionality of the QueryStatus method in MSHTML editing, you must include code that resembles the following:

var NOTSUPPORTED =  0;
var DISABLED     =  1;
var ENABLED      =  3;
var LATCHED      =  7;
var NINCHED      = 11;
var result = NOTSUPPORTED;
if(editor.document.queryCommandSupported(cmdName)) {
    result = editor.document.queryCommandEnabled(cmdName) ?
        ENABLED : DISABLED;
    if(ENABLED == result) {
        if(editor.document.queryCommandIndeterm(cmdName)){
            result = NINCHED;
        }
        else if(editor.document.queryCommandState(cmdName)) {
            result = LATCHED;
        }
    }
}

Additional notes about methods

The SetContextMenu method takes an array of strings and an array of states (MENU_UNCHECKED=0, MENU_CHECKED=1, and MENU_GRAY=2) and stores them until an oncontextmenu event is raised by the editor. To substitute for this method, use one of the many available HTML context menu implementations (most use the createPopup method).

If you are supporting the UseDivOnCarriageReturn property, the content you reload with is conditional on the property's value. If you are supporting the Dirty property, clear it when you create a new document.

The NewDocument method replaces the content of the editor with a blank document. To substitute for this command, you can use script such as the following:

var htmlP = "<html><head><title>New Document</title></head>" + 
    "<body><p>&nbsp;</p></body></html>";
var htmlD = "<html><head><title>New Document</title></head>" + 
    "<body><div>&nbsp;</div></body></html>";
var useDivOnCR = false;
function NewDocument() {
    editor.document.write(useDivOnCR ? htmlD : htmlP);
    editor.document.close();
}

To substitute for the LoadURL method, use code such as editor.src=url. If you are supporting the Dirty property, clear it when you load a new document.

The FilterSourceCode method is for source code preservation and cannot be reimplemented in script. If you need to define this method for compatibility, simply return its parameter, as shown in the following example.

function FilterSourceCode(str) { return str; }

The Refresh method invalidates the control window's rectangle and could be replaced by a method that does nothing, as shown in the following in a reimplementation.

function Refresh() {}

Properties

The following table lists properties from the safe-for-scripting DHTML Editing Control and whether they can be reimplemented using script in Internet Explorer.

Properties of the safe-for-scripting DHTML Editing Control

Property Data Type Direction Supportable?
CurrentDocumentPath String Out Yes
DOM Object Out Yes
DocumentHTML String In/Out Yes
IsDirty Boolean Out Partially
SourceCodePreservation Boolean In/Out No
ActivateApplets Boolean In/Out No
ActivateDTCs Boolean In/Out No
ActivateActiveXControls Boolean In/Out No
ShowDetails Boolean In/Out No
ShowBorders Boolean In/Out No
AbsoluteDropMode Boolean In/Out No
Scrollbars Boolean In/Out Yes
Appearance Special In/Out Yes
ScrollbarAppearance Special In/Out Yes
SnapToGridX Long In/Out No
SnapToGridY Long In/Out No
SnapToGrid Boolean In/Out No
BaseURL String In/Out Yes
DocumentTitle String Out Yes
UseDivOnCarriageReturn Boolean In/Out Yes
Busy Boolean Out Yes

Additional notes about properties

You can implement the CurrentDocumentPath property by returning editor.location.href.

The DOM property is equivalent to editor.document.

The DocumentHTML property can be implemented as editor.getElementsByTagName('HTML')[0].outerHTML.

The IsDirty property cannot be replaced exactly. You can approximate it by setting a global variable associated with the editor <iframe> element for all methods, properties, and commands that modify the document. Various events can cause the document to be dirty also, such as oncut, onpaste, ondrop, and onmove. For more information, see the notes for the DisplayChanged event under Events later in this document. Methods such as NewDocument and LoadURL must clear the global dirty flag.

The SourceCodePreservation, ActivateApplets, ActivateDTCs, ActivateActiveXControls, ShowDetails, ShowBorders, AbsoluteDropMode, SnapToGrid, SnapToGridX and SnapToGridY properties cannot be implemented in script.

The Appearance property sets the appearance of the border of the DHTML Editing Control itself. In your reimplementation, you can set editor.frameBorder.

The Scrollbars property can be implemented by setting editor.body.scroll to "Yes" or "No". It can be retrieved by using the comparison (editor.body.scroll=="no")

To reimplement the ScrollbarAppearance property, use CSS attributes. (For details, search the Web for "CSS flat scrollbar".)

The DocumentTitle property can be replaced with editor.document.title.

The UseDivOnCarriageReturn property was designed to help specify (indirectly) the behavior of the ENTER key. By default, pressing ENTER in an editable HTML area inserts a <p> element. In the DHTML Editing Control, when the ENTER key is pressed, the block element where the user is typing is split into two similar blocks. If the cursor is in a <p> element, another <p> element that has similar attributes is created. If the cursor is in a <div> element, another <div> element is created.

The UseDivOnCarriageReturn property specifies the default block element that is used when a blank document is created by calling the NewDocument method. If the property is set to true, the default block is a <div> element, with initial text similar to the following:

<HTML>
<HEAD><TITLE></TITLE></HEAD>
<BODY><DIV>&nbsp;</DIV></BODY>
</HTML>

Otherwise, the initial text will be similar to the following:

<HTML>
<HEAD><TITLE></TITLE></HEAD>
<BODY><P>&nbsp;</P></BODY>
</HTML> 

The Busy property indicates that the control is currently being loaded. This can be replaced with editor.document.readyState != "complete".

Reimplementing the BaseURL property

The BaseURL property sets the root part of the URL used for resolving relative URLs. There are significant differences between the way the DHTML Editing Control and MSHTML editing work in this respect. When the DHTML Editing Control was originally introduced in Internet Explorer 4, the document object model (DOM) did not include access to the <head> element, so a <base> element could not be added to the document programmatically. Instead, the control internally combines relative URLs with a base URL stored in the BaseURL property. In the current version of Internet Explorer, a <base> element can easily be added to the editor document, but using this technique causes behavior that differs from that of the control's BaseURL property, because the element becomes part of the editor.document property's content. Therefore, to simulate the control's behavior for the BaseURL property, you must remove <base> element from the document after the HTML content is obtained from the document's content. Reimplementing the BaseURL property is not straightforward, and you should test your application carefully to make sure that you are seeing the behavior you intend.

Another difference is that the DHTML Editing Control executes the IDM_NOFIXUPURLSONPASTE command, which is not available from script. This command prevents relative URLs from being changed to absolute URLs when content is pasted into an editable area. You can set the <base> element to override this fixup, or use a regular expression to correct this difference.

To get the <base> element tag value for a document, use script such as the following:

var baseHref = "";
var h = editor.document.getElementsByTagName("head");
if((undefined != h) && (0 < h.length)) {
    var b = h[0].getElementsByTagName("base");
    if((undefined != b) && (0 < b.length)) {
        baseHref = b[0].href;
    }
}

To set the <base> element for a document, use script such as the following:

var h = editor.document.getElementsByTagName("head");
if((undefined != h) && (0 < h.length)) {
    var b = h[0].getElementsByTagName("base");
    if((undefined == b) || (0 == b.length)) {
        var nb = editor.document.createElement("BASE");
        nb.href = newBaseHrefValue;
        h[0].insertAdjacentElement("afterBegin", nb);
    }
    else
        b[0].href = newBaseHrefValue;
}

To remove the <base> tag from the text returned from the editor.document property's content, use script such as the following:

var html = DocumentHTML();
var rex = new RegExp("<BASE[\\s]+[^>]*>", "i");
html = html.replace(rex, "");

Events

The following table lists events from the safe-for-scripting DHTML Editing Control and whether they can be reimplemented using script in Internet Explorer.

Events of the safe-for-scripting DHTML Editing Control

Event Supportable?
DocumentComplete Yes
DisplayChanged Partially
ShowContextMenu(xPos,yPos) Yes, with modification
ContextMenuAction(itemIndex) Yes, with modification
Onmousedown Yes
Onmousemove Yes
Onmouseup Yes
Onmouseout Yes
Onmouseover Yes
Onclick Yes
Ondblclick Yes
Onkeydown Yes
Onkeypress Yes
Onkeyup Yes
Onblur Yes
Onreadystatechange Yes

You can programmatically attach most events in script. The events onmousedown, onmousemove, onmouseup, onmouseout, onmouseover, onclick, ondblclick, onkeydown, onkeypress, and onkeyup can all be bound with syntax such as the following:

editor.document.attachEvent("EventName", EventHandlerFunction)

The onreadystatechange, onfocus, and onblur bindings might be reset when the contents of the <iframe> element are reloaded. Therefore, you can bind these events declaratively using attributes on the <iframe> element if you need them, which retains the bindings across reloads. The following example shows declarative bindings for these events:

<iframe ID="editor" SCROLLING="yes" onreadystatechange="editor_ReadyStateChange()"
  onfocus="editor_OnFocus" onblur="editor_Blur()"></iframe>

You can reimplement the functionality of the DocumentComplete event by handling the onreadystatechange event and including the test editor.document.readyState=="complete". This is an appropriate time to reattach other events (except those noted earlier) after a document reload.

The DisplayChanged event is not straightforward to reimplement. This event is raised very frequently in the DHTML Editing Control (including every time that the mouse moves), but it is absolutely necessary for keeping UI elements such as toolbars in the correct state. The DHTML Editing Control raises the DisplayChanged event in response to the OLECMDID_UPDATECOMMANDS command on the control's IOleCommandTarget object, which is inaccessible to script. To simulate the DisplayChanged event, raise it unconditionally in response to the onmousedown, onkeypress, onfocus, onreadystatechange, oncut, onpaste, ondrop, onmove, and onselectionchange events. In addition, it should be raised for onkeydown when the key is a cursor movement key, and on onkeyup when the key is one of the hotkeys that perform formatting changes. The DisplayChanged event should be raised when commands that set values are executed, and might be needed for onmousemove, depending on whether your application changes UI in response to the element which the pointer moves across.

You can detect that a hotkey has been pressed using code such as the following:

function IsHotKey(ev) {
    if(ev.ctrlKey && !ev.altKey) {
        var key = ev.keyCode;
        if((66 == key) || (73 == key) || (85 == key)) {
            return true;
        }
    }
    return false;
}

To detect that cursor movement keys have been pressed, use script such as the following:

function IsCursorMovementKey(key) {
    if(key >= 37 && key <= 40 || key == 9) {
        return true;
    }
    return false;
}

The ShowContextMenu event is roughly equivalent to the DHTML oncontextmenu event, but includes X and Y parameters. The HTML event model does not provide any way to include parameters with events. Therefore, you must change any handlers for the ShowContextMenu event; any references to the X and Y parameters can be replaced with editor.document.event.x and editor.document.event.y.

The ContextMenuAction event is similar to the ShowContextMenu event in that parameters cannot be included with events, so your code will require a modification. To replace it, create an event object by using createEventObject(editor.document.event) that you raise in response to a user selection. Set the event object's returnValue property to the ID of the menu item selected in your context menu implementation. In the hosting code, evaluate returnValue instead of the itemIndex parameter returned by the ContextMenuAction event.

CommandID Values

The following table lists the command IDs used with the ExecCommand and QueryStatus methods in the DHTML Editing Control, and the corresponding commands (if any) used with the DHTML execCommand method.

CommandID values used by ExecCommand and QueryStatus

DHTMLEdit Command Cmd ID execCommand Notes
DECMD_BOLD 5000 "Bold" Toggle
DECMD_COPY 5002 "Copy"  
DECMD_CUT 5003 "Cut"  
DECMD_DELETE 5004 "Delete"  
DECMD_DELETECELLS 5005   No table editing
DECMD_DELETECOLS 5006   No table editing
DECMD_DELETEROWS 5007   No table editing
DECMD_FINDTEXT 5008   No dialog boxes
DECMD_FONT 5009   No dialog boxes
DECMD_GETBACKCOLOR 5010 "BackColor" Special Value
DECMD_GETBLOCKFMT 5011   Reimplement
DECMD_GETBLOCKFMTNAMES 5012   Reimplement
DECMD_GETFONTNAME 5013 "FontName" Value
DECMD_GETFONTSIZE 5014 "FontSize" Value
DECMD_GETFORECOLOR 5015 "ForeColor" Special value
DECMD_HYPERLINK 5016 "CreateLink"  
DECMD_IMAGE 5017 "InsertImage"  
DECMD_INDENT 5018 "Indent"  
DECMD_INSERTCELL 5019   No table editing
DECMD_INSERTCOL 5020   No table editing
DECMD_INSERTROW 5021   No table editing
DECMD_INSERTTABLE 5022   No table editing
DECMD_ITALIC 5023 "Italic" Toggle
DECMD_JUSTIFYCENTER 5024 "JustifyCenter"  
DECMD_JUSTIFYLEFT 5025 "JustifyLeft"  
DECMD_JUSTIFYRIGHT 5026 "JustifyRight"  
DECMD_LOCK_ELEMENT 5027   Z-ordering
DECMD_MAKE_ABSOLUTE 5028 "2D-Position" Value
DECMD_MERGECELLS 5029   No table editing
DECMD_ORDERLIST 5030 "InsertOrderedList"  
DECMD_OUTDENT 5031 "Outdent"  
DECMD_PASTE 5032 "Paste"  
DECMD_REDO 5033   No undo
DECMD_REMOVEFORMAT 5034 "RemoveFormat"  
DECMD_SELECTALL 5035 "SelectAll"  
DECMD_SEND_BACKWARD 5036   Z-ordering
DECMD_BRING_FORWARD 5037   Z-ordering
DECMD_SEND_BELOW_TEXT 5038   Z-ordering
DECMD_BRING_ABOVE_TEXT 5039   Z-ordering
DECMD_SEND_TO_BACK 5040   Z-ordering
DECMD_BRING_TO_FRONT 5041   Z-ordering
DECMD_SETBACKCOLOR 5042 "BackColor" Value
DECMD_SETBLOCKFMT 5043 "FormatBlock" Value
DECMD_SETFONTNAME 5044 "FontName" Value
DECMD_SETFONTSIZE 5045 "FontSize" Value
DECMD_SETFORECOLOR 5046 "ForeColor" Value
DECMD_SPLITCELL 5047   No table editing
DECMD_UNDERLINE 5048 "Underline" Toggle
DECMD_UNDO 5049   No undo
DECMD_UNLINK 5050 "Unlink"  
DECMD_UNORDERLIST 5051 "InsertUnorderedList"  

Named commands (the commands that have a name in the execCommand column in the preceding table) can be executed using one of the following calls: editor.document.execCommand(commandName) or editor.document.execCommand(commandName, 0, parameter). The functionality represented by the unnamed commands is not available in MSHMTL editing, except those noted as "Reimplement."

The unnamed commands available through the ExecCommand and QueryStatus methods of the DHTML Editing Control cannot be reimplemented because of the following limitations imposed by MSHTML editing in Internet Explorer:

  • Undo. Undo and redo commands are not supported.
  • Z-ordering. Z-order commands such as Send Backward and Bring Forward can be implemented. However, because designMode editing does not support repositioning and resizing absolutely positioned elements, 3-D editing is too weak to be usable, so those commands will not be covered in this document.
  • Table editing. The DHTML Editing Control included some table editing commands. These are not supported by MSHMTL editing, but a table object model is available that can help in reimplementing these commands if necessary.

Reading block format information

The equivalent of the DECMD_GETBLOCKFMT command is not supported in MSHMTL editing. In the control, this command returns the name of the block that contains the current selection, using names used by the DECMD_SETBLOCKFMT and DECMD_GETBLOCKFMTNAMES commands. A command like DECMD_GETBLOCKFMT is required for updating the command bar UI for formatting blocks. You can approximate its functionality by using script such as the following:

var _blockFormats = {
    "BODY"    : "Normal",
    "P"       : "Normal",
    "PRE"     : "Formatted",
    "ADDRESS" : "Address",
    "H1"      : "Heading 1",
    "H2"      : "Heading 2",
    "H3"      : "Heading 3",
    "H4"      : "Heading 4",
    "H5"      : "Heading 5",
    "H6"      : "Heading 6",
    "OL"      : "Numbered List",
    "UL"      : "Bulleted List",
    "DIR"     : "Directory List",
    "MENU"    : "Menu List",
    "DT"      : "Definition Term",
    "DD"      : "Definition"
};

function GetBlockFmt() {
    try {
        var sel = editor.document.selection;
        if(undefined != sel) {
            var selEl = sel.createRange().parentElement();
            do {
                if(undefined == selEl) {
                    return "Normal";
                }
                var item = _blockFormats[selEl.tagName];
                if(undefined != item) {
                    return item;
                }
                selEl = selEl.parentElement;
            } while(true);
        }
    }
    catch(e) {
    }
    return "Normal";
}

The GetBlockFmtNames command is unusual in the DHTML Editing Control because it returns a custom helper object that enumerates the names. This was necessary for compatibility between Visual Basic and JScript, but is not needed in any reimplementation. You can use the Dialog Helper Object ({3050f819-98b5-11cf-bb82-00aa00bdce0b}) to get an enumerated list of block format names and a list of fonts. For more information, search the Web for "Dialog Helper Object". You will have to modify your hosting code to enumerate this collection instead of accessing the original helper object.

Additional Reimplementation Considerations

This section discusses some additional considerations for reimplementing the functionality of the DHTML Editing Control.

Dialog boxes

The Find and Font dialog boxes, which are implemented in Internet Explorer and which the control invokes using command IDs, are not exposed to script. If you have UI for picking fonts, you need to replace it with a list of font names that you get from the Dialog Helper Object.

You can implement the Find command using script such as the following:

function Find() {
    var rng = editor.document.body.createTextRange();
    var txt = prompt("Enter text to search for", "");
    var found = rng.findText(txt);
    if(found) {
        rng.select();
    }
}

Color values

The DHTML BackColor and ForeColor properties return a decimal value, whereas the DHTML Editing Control returns a hexadecimal string. If your code relies on hexadecimal color values, use script such as the following to convert the decimal values to hexadecimal.

function DecToHexColor(dVal) {
    var hVal = dVal.toString(16);
    if (hVal.length < 6) {
        var temp = "000000".substring(0,6-hVal.length);
        hVal = temp.concat(hVal);
    }
    return hVal;
}

Named commands

The remaining commands from the preceding table have names that can be passed to the editor.document.execCommand method. For details about each command, see the documentation on command identifiers available from the Methods page on the Microsoft Web site.

Commands marked "Toggle" in the preceding table take no parameters and toggle the state of the current selection when it is executed. Commands marked "Value" require a value when a set command is called and return a value when a get command is called. Commands without notes are executed without parameters.

Using an HTML Component

As an example of a real-world reimplementation, we retrofitted Microsoft Outlook Web Access (OWA) to work without the safe-for-scripting DHTML Editing Control. The control had been embedded in the Web application for several versions, so the code was heavily reliant on the object model of the control.

An important requirement was to be able to reimplement the behavior of the control while making as few changes to the hosting application as possible. We therefore used an Internet Explorer HTML Component (HTC)—a behavior. This is a component that is written in DHTML and in script, and that can then be instantiated on a Web page by using a custom markup tag. In effect, not only did we reimplement the functionality of the DHTML Editing Control, we simulated the control with this HTC. The result was that we needed to make only a handful of changes to the supporting code, and the effort invested in OWA was potentially transferable to other applications.

To avoid possible collisions between elements defined in the HTC script and those on the page, we implemented the behavior as a "view-linked" HTC. This encapsulates the behavior's elements by making them invisible to the hosting application.

OWA had already been coded to redirect to different pages for specific browsers, so we were able to create new templates that delivered appropriate pages to specific browsers. Simulating the control reduced the need to modify the code supporting the templates.

A declaration of an HTC in a file named Surrogate.htc might resemble the following:

<PUBLIC:COMPONENT tagName="SurrogateDhtmlEditor" supportsEditMode="true">
<PUBLIC:DEFAULTS tabStop="true" viewMasterTab="false"
  viewLinkContent="true"/>
  ...
  <PUBLIC:METHOD NAME="MethodName"/>
  ...
  <PUBLIC:PROPERTY NAME="PropName" PUT="put_Prop" GET="get_Prop"/>
  ...
</PUBLIC:COMPONENT>

<IFRAME ID="editor"
  SCROLLING="yes"
  onreadystatechange="EditorReadyStateChange()"
  onfocus="EditorFocus()"
  onblur="EditorBlur()">
</IFRAME>

<OBJECT ID="dlgHelper" WIDTH="0px" HEIGHT="0px"
  CLASSID="clsid:3050f819-98b5-11cf-bb82-00aa00bdce0b">
</OBJECT>
<SCRIPT language="javascript" src="SurrogateDhtmlEditor.js">

To instantiate this HTC in a Web page, you can do the following:

<HTML xmlns:editing>
<HEAD>
<?IMPORT namespace="editing" implementation="controls/surrogate.htc" >
...
</HEAD>
<BODY>
...
<editing:SurrogateDhtmlEditor class="EditorClass" id="editor" height="200" width="100%" />
...
</BODY>
</HTML>

The following table provides notes about the elements illustrated in the preceding example that you would replace with your own values.

Element Notes
xmlns:editing Choose any name that you want for the XML namespace.
namespace="editing" This must match the XML namespace you specified.
implementation="controls/surrogate.htc" This is the relative path to the HTC file that you are using.
<editing:SurrogateDhtmlEditor> The tag prefix (editing) must match the XML namespace you specified. The tag name (SurrogateDhtmlEditor) must match the value of the tagName attribute in the first line of the HTC file.
id="editor" This is the ID that you will use to refer to the editing component in your application.

By using an HTC, we were able to maintain all the functionality we had originally implemented with the DHTML Editing Control in a way that would be compatible with Windows Vista. The HTC did not implement all the functionality of the DHTML Editing Control, but we were able to implement all the features that OWA required.

Summary

The safe-for-scripting DHTML Editing Control will not be available in Windows Vista. If you have Web applications that currently rely on the control, you can do the following:

  • Look for a third-party replacement control.
  • Reimplement the functionality that you need with an <iframe> element and script.
  • Replace the control with an Internet Explorer HTML component (HTC, a behavior) written in script that encapsulates the functionality that you need. We were able to use this approach successfully in one of the most prominent applications that relied on the safe-for-scripting DHTML Editing Control, namely Outlook Web Access.

The DHTML Editing Control for Applications and the TriEdit ActiveX Document objects will also not be included in Windows Vista, but will be available for download separately, and can be licensed for distribution. After these are installed on Windows Vista, the user experience with the control or document object is identical to that on earlier versions of Windows.