JSEditor.cpp   [plain text]


/*
 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2007 Trolltech ASA
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "JSEditor.h"

#include "CSSPropertyNames.h"
#include "CreateLinkCommand.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "Editor.h"
#include "EditorClient.h"
#include "FormatBlockCommand.h"
#include "Frame.h"
#include "HTMLFontElement.h"
#include "HTMLImageElement.h"
#include "HTMLNames.h"
#include "IndentOutdentCommand.h"
#include "InsertListCommand.h"
#include "Page.h"
#include "ReplaceSelectionCommand.h"
#include "SelectionController.h"
#include "Settings.h"
#include "TypingCommand.h"
#include "UnlinkCommand.h"
#include "htmlediting.h"
#include "markup.h"

namespace WebCore {

using namespace HTMLNames;

class Document;

namespace {

struct CommandImp {
    bool (*execFn)(Frame*, bool userInterface, const String& value);
    bool (*enabledFn)(Frame*);
    Frame::TriState (*stateFn)(Frame*);
    String (*valueFn)(Frame*);
};

typedef HashMap<StringImpl*, const CommandImp*, CaseInsensitiveHash<StringImpl*> > CommandMap;

CommandMap* createCommandDictionary();

const CommandImp* commandImp(const String& command)
{
    static CommandMap* commandDictionary = createCommandDictionary();
    return commandDictionary->get(command.impl());
}

} // anonymous namespace

bool JSEditor::execCommand(const String& command, bool userInterface, const String& value)
{
    const CommandImp* cmd = commandImp(command);
    if (!cmd)
        return false;
    Frame* frame = m_document->frame();
    if (!frame)
        return false;
    m_document->updateLayoutIgnorePendingStylesheets();
    return cmd->enabledFn(frame) && cmd->execFn(frame, userInterface, value);
}

bool JSEditor::queryCommandEnabled(const String& command)
{
    const CommandImp* cmd = commandImp(command);
    if (!cmd)
        return false;
    Frame* frame = m_document->frame();
    if (!frame)
        return false;
    m_document->updateLayoutIgnorePendingStylesheets();
    return cmd->enabledFn(frame);
}

bool JSEditor::queryCommandIndeterm(const String& command)
{
    const CommandImp* cmd = commandImp(command);
    if (!cmd)
        return false;
    Frame* frame = m_document->frame();
    if (!frame)
        return false;
    m_document->updateLayoutIgnorePendingStylesheets();
    return cmd->stateFn(frame) == Frame::mixedTriState;
}

bool JSEditor::queryCommandState(const String& command)
{
    const CommandImp* cmd = commandImp(command);
    if (!cmd)
        return false;
    Frame* frame = m_document->frame();
    if (!frame)
        return false;
    m_document->updateLayoutIgnorePendingStylesheets();
    return cmd->stateFn(frame) != Frame::falseTriState;
}

bool JSEditor::queryCommandSupported(const String& command)
{
    Settings* settings = m_document->settings();
    if ((!settings || !settings->isDOMPasteAllowed()) && command.lower() == "paste")
        return false;
    return commandImp(command) != 0;
}

String JSEditor::queryCommandValue(const String& command)
{
    const CommandImp* cmd = commandImp(command);
    if (!cmd)
        return String();
    Frame* frame = m_document->frame();
    if (!frame)
        return String();
    m_document->updateLayoutIgnorePendingStylesheets();
    return cmd->valueFn(frame);
}

// =============================================================================================

// Private stuff, all inside an anonymous namespace.

namespace {

bool execStyleChange(Frame* frame, int propertyID, const String& propertyValue)
{
    RefPtr<CSSMutableStyleDeclaration> style = new CSSMutableStyleDeclaration;
    style->setProperty(propertyID, propertyValue);
    frame->editor()->applyStyle(style.get());
    return true;
}

bool execStyleChange(Frame* frame, int propertyID, const char* propertyValue)
{
    return execStyleChange(frame, propertyID, String(propertyValue));
}

bool execStyleChange(Frame* frame, int propertyID, int propertyValue)
{
    RefPtr<CSSMutableStyleDeclaration> style = new CSSMutableStyleDeclaration;
    style->setProperty(propertyID, propertyValue);
    frame->editor()->applyStyle(style.get());
    return true;
}

Frame::TriState stateStyle(Frame* frame, int propertyID, const char* desiredValue)
{
    RefPtr<CSSMutableStyleDeclaration> style = new CSSMutableStyleDeclaration;
    style->setProperty(propertyID, desiredValue);
    return frame->selectionHasStyle(style.get());
}

bool selectionStartHasStyle(Frame* frame, int propertyID, const char* desiredValue)
{
    RefPtr<CSSMutableStyleDeclaration> style = new CSSMutableStyleDeclaration;
    style->setProperty(propertyID, desiredValue);
    return frame->editor()->selectionStartHasStyle(style.get());
}

String valueStyle(Frame* frame, int propertyID)
{
    return frame->selectionStartStylePropertyValue(propertyID);
}

// =============================================================================================
//
// execCommand implementations
//

bool execBackColor(Frame* frame, bool, const String& value)
{
    return execStyleChange(frame, CSS_PROP_BACKGROUND_COLOR, value);
}

bool execBold(Frame* frame, bool, const String&)
{
    bool isBold = selectionStartHasStyle(frame, CSS_PROP_FONT_WEIGHT, "bold");
    return execStyleChange(frame, CSS_PROP_FONT_WEIGHT, isBold ? "normal" : "bold");
}

bool execCopy(Frame* frame, bool, const String&)
{
    frame->editor()->copy();
    return true;
}

bool execCreateLink(Frame* frame, bool userInterface, const String& value)
{
    // FIXME: If userInterface is true, we should display a dialog box to let the user enter a url.
    if (userInterface)
        LOG_ERROR("A dialog box for link creation is not yet implemented.\n");
    
    if (value.isEmpty())
        return false;
    
    applyCommand(new CreateLinkCommand(frame->document(), value));
    return true;
}

bool execCut(Frame* frame, bool, const String&)
{
    frame->editor()->cut();
    return true;
}

bool execDelete(Frame* frame, bool, const String&)
{
    TypingCommand::deleteKeyPressed(frame->document(), frame->selectionGranularity() == WordGranularity);
    return true;
}

bool execFindString(Frame* frame, bool, const String& value)
{
    return frame->findString(value, true, false, true, false);
}

bool execForwardDelete(Frame* frame, bool, const String&)
{
    TypingCommand::forwardDeleteKeyPressed(frame->document());
    return true;
}

bool execFontName(Frame* frame, bool, const String& value)
{
    return execStyleChange(frame, CSS_PROP_FONT_FAMILY, value);
}

bool execFontSize(Frame* frame, bool, const String& value)
{
    int size;
    if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size))
        return false;
        
    return execStyleChange(frame, CSS_PROP_FONT_SIZE, size);
}

bool execFontSizeDelta(Frame* frame, bool, const String& value)
{
    return execStyleChange(frame, CSS_PROP__WEBKIT_FONT_SIZE_DELTA, value);
}

bool execForeColor(Frame* frame, bool, const String& value)
{
    return execStyleChange(frame, CSS_PROP_COLOR, value);
}

bool execFormatBlock(Frame* frame, bool, const String& value)
{
    String tagName = value.lower();
    if (tagName[0] == '<' && tagName[tagName.length() - 1] == '>')
        tagName = tagName.substring(1, tagName.length() - 2);
    if (!validBlockTag(tagName))
        return false;
    applyCommand(new FormatBlockCommand(frame->document(), tagName));
    return true;
}

bool execInsertHorizontalRule(Frame* frame, bool userInterface, const String& value)
{
    RefPtr<HTMLElement> hr = new HTMLElement(hrTag, frame->document());
    hr->setId(value);
    RefPtr<DocumentFragment> fragment = new DocumentFragment(frame->document());
    ExceptionCode ec = 0;
    fragment->appendChild(hr, ec);
    if (ec)
        return false;
    
    applyCommand(new ReplaceSelectionCommand(frame->document(), fragment.release(),
        false, false, false, true, false, EditActionUnspecified));
    return true;
}

bool execInsertHTML(Frame* frame, bool userInterface, const String& value)
{
    Document* document = frame->document();
    applyCommand(new ReplaceSelectionCommand(frame->document(), createFragmentFromMarkup(document, value, ""), false));
    return true;
}

bool execInsertImage(Frame* frame, bool userInterface, const String& value)
{
    // FIXME: If userInterface is true, we should display a dialog box and let the user choose a local image.
    if (userInterface)
        LOG_ERROR("A dialog box for image insertion is not yet implemented.\n");
    
    RefPtr<HTMLImageElement> image = new HTMLImageElement(imgTag, frame->document());
    image->setSrc(value);
    RefPtr<DocumentFragment> fragment = new DocumentFragment(frame->document());
    ExceptionCode ec = 0;
    fragment->appendChild(image, ec);
    if (ec)
        return false;
    
    applyCommand(new ReplaceSelectionCommand(frame->document(), fragment.release(), false));
    return true;
}

bool execIndent(Frame* frame, bool, const String&)
{
    applyCommand(new IndentOutdentCommand(frame->document(), IndentOutdentCommand::Indent));
    return true;
}

bool execInsertLineBreak(Frame* frame, bool, const String&)
{
    TypingCommand::insertLineBreak(frame->document());
    return true;
}

bool execInsertParagraph(Frame* frame, bool, const String&)
{
    TypingCommand::insertParagraphSeparator(frame->document());
    return true;
}

bool execInsertNewlineInQuotedContent(Frame* frame, bool, const String&)
{
    TypingCommand::insertParagraphSeparatorInQuotedContent(frame->document());
    return true;
}

bool execInsertText(Frame* frame, bool, const String& value)
{
    TypingCommand::insertText(frame->document(), value);
    return true;
}

bool execInsertUnorderedList(Frame* frame, bool, const String& value)
{
    applyCommand(new InsertListCommand(frame->document(), InsertListCommand::UnorderedList, value));
    return true;
}

bool execInsertOrderedList(Frame* frame, bool, const String& value)
{
    applyCommand(new InsertListCommand(frame->document(), InsertListCommand::OrderedList, value));
    return true;
}

bool execItalic(Frame* frame, bool, const String&)
{
    bool isItalic = selectionStartHasStyle(frame, CSS_PROP_FONT_STYLE, "italic");
    return execStyleChange(frame, CSS_PROP_FONT_STYLE, isItalic ? "normal" : "italic");
}

bool execJustifyCenter(Frame* frame, bool, const String&)
{
    return execStyleChange(frame, CSS_PROP_TEXT_ALIGN, "center");
}

bool execJustifyFull(Frame* frame, bool, const String&)
{
    return execStyleChange(frame, CSS_PROP_TEXT_ALIGN, "justify");
}

bool execJustifyLeft(Frame* frame, bool, const String&)
{
    return execStyleChange(frame, CSS_PROP_TEXT_ALIGN, "left");
}

bool execJustifyRight(Frame* frame, bool, const String&)
{
    return execStyleChange(frame, CSS_PROP_TEXT_ALIGN, "right");
}

bool execOutdent(Frame* frame, bool, const String&)
{
    applyCommand(new IndentOutdentCommand(frame->document(), IndentOutdentCommand::Outdent));
    return true;
}

bool execPaste(Frame* frame, bool, const String&)
{
    frame->editor()->paste();
    return true;
}

bool execPasteAndMatchStyle(Frame* frame, bool, const String&)
{
    frame->editor()->pasteAsPlainText();
    return true;
}

bool execPrint(Frame* frame, bool, const String&)
{
    if (Page* page = frame->page())
        page->chrome()->print(frame);
    return true;
}

bool execRedo(Frame* frame, bool, const String&)
{
    frame->editor()->redo();
    return true;
}

bool execRemoveFormat(Frame* frame, bool userInterface, const String& value)
{
    frame->editor()->removeFormattingAndStyle();
    return true;
}

bool execSelectAll(Frame* frame, bool, const String&)
{
    frame->selectionController()->selectAll();
    return true;
}

bool execStrikethrough(Frame* frame, bool, const String&)
{
    bool isStrikethrough = selectionStartHasStyle(frame,  CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT, "line-through");
    return execStyleChange(frame, CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT, isStrikethrough ? "none" : "line-through");
}

bool execSubscript(Frame* frame, bool, const String&)
{
    return execStyleChange(frame,  CSS_PROP_VERTICAL_ALIGN, "sub");
}

bool execSuperscript(Frame* frame, bool, const String&)
{
    return execStyleChange(frame,  CSS_PROP_VERTICAL_ALIGN, "super");
}

bool execTranspose(Frame* frame, bool, const String&)
{
    frame->transpose();
    return true;
}

bool execUnderline(Frame* frame, bool, const String&)
{
    bool isUnderlined = selectionStartHasStyle(frame,  CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT, "underline");
    return execStyleChange(frame,  CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT, isUnderlined ? "none" : "underline");
}

bool execUndo(Frame* frame, bool, const String&)
{
    frame->editor()->undo();
    return true;
}

bool execUnlink(Frame* frame, bool, const String&)
{
    applyCommand(new UnlinkCommand(frame->document()));
    return true;
}

bool execUnselect(Frame* frame, bool, const String&)
{
    frame->selectionController()->clear();
    return true;
}

// =============================================================================================
//
// queryCommandEnabled implementations
//
// It's a bit difficult to get a clear notion of the difference between
// "supported" and "enabled" from reading the Microsoft documentation, but
// what little I could glean from that seems to make some sense.
//     Supported = The command is supported by this object.
//     Enabled =   The command is available and enabled.

bool enabled(Frame*)
{
    return true;
}

bool enabledAnyCaret(Frame* frame)
{
    return frame->selectionController()->isCaret() && frame->selectionController()->isContentEditable();
}

bool enabledAnySelection(Frame* frame)
{
    return frame->selectionController()->isCaretOrRange();
}

bool enabledAnyEditableSelection(Frame* frame)
{
    return frame->selectionController()->isCaretOrRange() && frame->selectionController()->isContentEditable();
}

bool enabledAnyRichlyEditableSelection(Frame* frame)
{
    return frame->selectionController()->isCaretOrRange() && frame->selectionController()->isContentRichlyEditable();
}

bool enabledCopy(Frame* frame)
{
    return !frame->selectionController()->isInPasswordField();
}

bool enabledPaste(Frame* frame)
{
    Settings* settings = frame ? frame->settings() : 0;
    return settings && settings->isDOMPasteAllowed() && frame->editor()->canPaste();
}

bool enabledAnyRangeSelection(Frame* frame)
{
    return frame->selectionController()->isRange();
}

bool enabledAnyEditableRangeSelection(Frame* frame)
{
    return frame->selectionController()->isRange() && frame->selectionController()->isContentEditable();
}

bool enabledAnyRichlyEditableRangeSelection(Frame* frame)
{
    return frame->selectionController()->isRange() && frame->selectionController()->isContentRichlyEditable();
}

bool enabledRedo(Frame* frame)
{
    return frame->editor()->canRedo();
}

bool enabledUndo(Frame* frame)
{
    return frame->editor()->canUndo();
}

// =============================================================================================
//
// queryCommandIndeterm/State implementations
//
// It's a bit difficult to get a clear notion of what these methods are supposed
// to do from reading the Microsoft documentation, but my current guess is this:
//
//     queryCommandState and queryCommandIndeterm work in concert to return
//     the two bits of information that are needed to tell, for instance,
//     if the text of a selection is bold. The answer can be "yes", "no", or
//     "partially".
//
// If this is so, then queryCommandState should return "yes" in the case where
// all the text is bold and "no" for non-bold or partially-bold text.
// Then, queryCommandIndeterm should return "no" in the case where
// all the text is either all bold or not-bold and and "yes" for partially-bold text.

Frame::TriState stateNone(Frame*)
{
    return Frame::falseTriState;
}

Frame::TriState stateBold(Frame* frame)
{
    return stateStyle(frame, CSS_PROP_FONT_WEIGHT, "bold");
}

Frame::TriState stateItalic(Frame* frame)
{
    return stateStyle(frame, CSS_PROP_FONT_STYLE, "italic");
}

Frame::TriState stateUnorderedList(Frame* frame)
{
    return frame->editor()->selectionUnorderedListState();
}

Frame::TriState stateOrderedList(Frame* frame)
{
    return frame->editor()->selectionOrderedListState();
}

Frame::TriState stateStrikethrough(Frame* frame)
{
    return stateStyle(frame, CSS_PROP_TEXT_DECORATION, "line-through");
}

Frame::TriState stateSubscript(Frame* frame)
{
    return stateStyle(frame, CSS_PROP_VERTICAL_ALIGN, "sub");
}

Frame::TriState stateSuperscript(Frame* frame)
{
    return stateStyle(frame, CSS_PROP_VERTICAL_ALIGN, "super");
}

Frame::TriState stateUnderline(Frame* frame)
{
    return stateStyle(frame, CSS_PROP_TEXT_DECORATION, "underline");
}

// =============================================================================================
//
// queryCommandValue implementations
//

String valueNull(Frame*)
{
    return String();
}

String valueBackColor(Frame* frame)
{
    return valueStyle(frame, CSS_PROP_BACKGROUND_COLOR);
}

String valueFontName(Frame* frame)
{
    return valueStyle(frame, CSS_PROP_FONT_FAMILY);
}

String valueFontSize(Frame* frame)
{
    return valueStyle(frame, CSS_PROP_FONT_SIZE);
}

String valueFontSizeDelta(Frame* frame)
{
    return valueStyle(frame, CSS_PROP__WEBKIT_FONT_SIZE_DELTA);
}

String valueForeColor(Frame* frame)
{
    return valueStyle(frame, CSS_PROP_COLOR);
}

// =============================================================================================

CommandMap* createCommandDictionary()
{
    struct EditorCommand { const char* name; CommandImp imp; };

    static const EditorCommand commands[] = {

        { "BackColor", { execBackColor, enabledAnyRichlyEditableRangeSelection, stateNone, valueBackColor } },
        { "Bold", { execBold, enabledAnyRichlyEditableSelection, stateBold, valueNull } },
        { "Copy", { execCopy, enabledCopy, stateNone, valueNull } },
        { "CreateLink", { execCreateLink, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "Cut", { execCut, enabledAnyEditableRangeSelection, stateNone, valueNull } },
        { "Delete", { execDelete, enabledAnyEditableSelection, stateNone, valueNull } },
        { "FindString", { execFindString, enabled, stateNone, valueNull } },
        { "FontName", { execFontName, enabledAnySelection, stateNone, valueFontName } },
        { "FontSize", { execFontSize, enabledAnySelection, stateNone, valueFontSize } },
        { "FontSizeDelta", { execFontSizeDelta, enabledAnySelection, stateNone, valueFontSizeDelta } },
        { "ForeColor", { execForeColor, enabledAnySelection, stateNone, valueForeColor } },
        { "FormatBlock", { execFormatBlock, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "ForwardDelete", { execForwardDelete, enabledAnyEditableSelection, stateNone, valueNull } },
        { "HiliteColor", { execBackColor, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "Indent", { execIndent, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "InsertHorizontalRule", { execInsertHorizontalRule, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "InsertHTML", { execInsertHTML, enabledAnyEditableSelection, stateNone, valueNull } },
        { "InsertImage", { execInsertImage, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "InsertLineBreak", { execInsertLineBreak, enabledAnyEditableSelection, stateNone, valueNull } },
        { "InsertOrderedList", { execInsertOrderedList, enabledAnyRichlyEditableSelection, stateOrderedList, valueNull } },
        { "InsertParagraph", { execInsertParagraph, enabledAnyEditableSelection, stateNone, valueNull } },
        { "InsertNewlineInQuotedContent", { execInsertNewlineInQuotedContent, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "InsertText", { execInsertText, enabledAnyEditableSelection, stateNone, valueNull } },
        { "InsertUnorderedList", { execInsertUnorderedList, enabledAnyRichlyEditableSelection, stateUnorderedList, valueNull } },
        { "Italic", { execItalic, enabledAnyRichlyEditableSelection, stateItalic, valueNull } },
        { "JustifyCenter", { execJustifyCenter, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "JustifyFull", { execJustifyFull, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "JustifyLeft", { execJustifyLeft, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "JustifyNone", { execJustifyLeft, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "JustifyRight", { execJustifyRight, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "Outdent", { execOutdent, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
        { "Paste", { execPaste, enabledPaste, stateNone, valueNull } },
        { "PasteAndMatchStyle", { execPasteAndMatchStyle, enabledPaste, stateNone, valueNull } },
        { "Print", { execPrint, enabled, stateNone, valueNull } },
        { "Redo", { execRedo, enabledRedo, stateNone, valueNull } },
        { "RemoveFormat", { execRemoveFormat, enabledAnyEditableRangeSelection, stateNone, valueNull } },
        { "SelectAll", { execSelectAll, enabled, stateNone, valueNull } },
        { "Strikethrough", { execStrikethrough, enabledAnyRichlyEditableSelection, stateStrikethrough, valueNull } },
        { "Subscript", { execSubscript, enabledAnyRichlyEditableSelection, stateSubscript, valueNull } },
        { "Superscript", { execSuperscript, enabledAnyRichlyEditableSelection, stateSuperscript, valueNull } },
        { "Transpose", { execTranspose, enabledAnyCaret, stateNone, valueNull } },
        { "Underline", { execUnderline, enabledAnyRichlyEditableSelection, stateUnderline, valueNull } },
        { "Undo", { execUndo, enabledUndo, stateNone, valueNull } },
        { "Unlink", { execUnlink, enabledAnyRichlyEditableRangeSelection, stateNone, valueNull } },
        { "Unselect", { execUnselect, enabledAnySelection, stateNone, valueNull } }

        //
        // The "unsupported" commands are listed here since they appear in the Microsoft
        // documentation used as the basis for the list.
        //

        // 2D-Position (not supported)
        // AbsolutePosition (not supported)
        // BlockDirLTR (not supported)
        // BlockDirRTL (not supported)
        // BrowseMode (not supported)
        // ClearAuthenticationCache (not supported)
        // CreateBookmark (not supported)
        // DirLTR (not supported)
        // DirRTL (not supported)
        // EditMode (not supported)
        // InlineDirLTR (not supported)
        // InlineDirRTL (not supported)
        // InsertButton (not supported)
        // InsertFieldSet (not supported)
        // InsertIFrame (not supported)
        // InsertInputButton (not supported)
        // InsertInputCheckbox (not supported)
        // InsertInputFileUpload (not supported)
        // InsertInputHidden (not supported)
        // InsertInputImage (not supported)
        // InsertInputPassword (not supported)
        // InsertInputRadio (not supported)
        // InsertInputReset (not supported)
        // InsertInputSubmit (not supported)
        // InsertInputText (not supported)
        // InsertMarquee (not supported)
        // InsertSelectDropDown (not supported)
        // InsertSelectListBox (not supported)
        // InsertTextArea (not supported)
        // LiveResize (not supported)
        // MultipleSelection (not supported)
        // Open (not supported)
        // Overwrite (not supported)
        // PlayImage (not supported)
        // Refresh (not supported)
        // RemoveParaFormat (not supported)
        // SaveAs (not supported)
        // SizeToControl (not supported)
        // SizeToControlHeight (not supported)
        // SizeToControlWidth (not supported)
        // Stop (not supported)
        // StopImage (not supported)
        // Unbookmark (not supported)
    };

    CommandMap* commandMap = new CommandMap;

    const int numCommands = sizeof(commands) / sizeof(commands[0]);
    for (int i = 0; i < numCommands; ++i) {
        StringImpl *name = new StringImpl(commands[i].name);
        name->ref();
        commandMap->set(name, &commands[i].imp);
    }
    return commandMap;
}

} // anonymous namespace

} // namespace WebCore