Home | History | Annotate | Download | only in editing
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
      3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      4  * Copyright (C) 2009 Igalia S.L.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "config.h"
     29 #include "core/editing/Editor.h"
     30 
     31 #include "CSSPropertyNames.h"
     32 #include "CSSValueKeywords.h"
     33 #include "HTMLNames.h"
     34 #include "bindings/v8/ExceptionState.h"
     35 #include "bindings/v8/ExceptionStatePlaceholder.h"
     36 #include "core/css/CSSValueList.h"
     37 #include "core/css/StylePropertySet.h"
     38 #include "core/dom/DocumentFragment.h"
     39 #include "core/events/Event.h"
     40 #include "core/editing/CreateLinkCommand.h"
     41 #include "core/editing/FormatBlockCommand.h"
     42 #include "core/editing/IndentOutdentCommand.h"
     43 #include "core/editing/InsertListCommand.h"
     44 #include "core/editing/ReplaceSelectionCommand.h"
     45 #include "core/editing/SpellChecker.h"
     46 #include "core/editing/TypingCommand.h"
     47 #include "core/editing/UnlinkCommand.h"
     48 #include "core/editing/markup.h"
     49 #include "core/html/HTMLFontElement.h"
     50 #include "core/html/HTMLHRElement.h"
     51 #include "core/html/HTMLImageElement.h"
     52 #include "core/page/Chrome.h"
     53 #include "core/page/EditorClient.h"
     54 #include "core/page/EventHandler.h"
     55 #include "core/frame/Frame.h"
     56 #include "core/frame/FrameView.h"
     57 #include "core/page/Page.h"
     58 #include "core/frame/Settings.h"
     59 #include "core/platform/Pasteboard.h"
     60 #include "core/rendering/RenderBox.h"
     61 #include "platform/KillRing.h"
     62 #include "platform/scroll/Scrollbar.h"
     63 #include "wtf/text/AtomicString.h"
     64 
     65 namespace WebCore {
     66 
     67 using namespace HTMLNames;
     68 
     69 class EditorInternalCommand {
     70 public:
     71     bool (*execute)(Frame&, Event*, EditorCommandSource, const String&);
     72     bool (*isSupportedFromDOM)(Frame*);
     73     bool (*isEnabled)(Frame&, Event*, EditorCommandSource);
     74     TriState (*state)(Frame&, Event*);
     75     String (*value)(Frame&, Event*);
     76     bool isTextInsertion;
     77     bool allowExecutionWhenDisabled;
     78 };
     79 
     80 typedef HashMap<String, const EditorInternalCommand*, CaseFoldingHash> CommandMap;
     81 
     82 static const bool notTextInsertion = false;
     83 static const bool isTextInsertion = true;
     84 
     85 static const bool allowExecutionWhenDisabled = true;
     86 static const bool doNotAllowExecutionWhenDisabled = false;
     87 
     88 // Related to Editor::selectionForCommand.
     89 // Certain operations continue to use the target control's selection even if the event handler
     90 // already moved the selection outside of the text control.
     91 static Frame* targetFrame(Frame& frame, Event* event)
     92 {
     93     if (!event)
     94         return &frame;
     95     Node* node = event->target()->toNode();
     96     if (!node)
     97         return &frame;
     98     return node->document().frame();
     99 }
    100 
    101 static bool applyCommandToFrame(Frame& frame, EditorCommandSource source, EditAction action, StylePropertySet* style)
    102 {
    103     // FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that?
    104     switch (source) {
    105     case CommandFromMenuOrKeyBinding:
    106         frame.editor().applyStyleToSelection(style, action);
    107         return true;
    108     case CommandFromDOM:
    109     case CommandFromDOMWithUserInterface:
    110         frame.editor().applyStyle(style);
    111         return true;
    112     }
    113     ASSERT_NOT_REACHED();
    114     return false;
    115 }
    116 
    117 static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue)
    118 {
    119     RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
    120     style->setProperty(propertyID, propertyValue);
    121     return applyCommandToFrame(frame, source, action, style.get());
    122 }
    123 
    124 static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValueID propertyValue)
    125 {
    126     RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
    127     style->setProperty(propertyID, propertyValue);
    128     return applyCommandToFrame(frame, source, action, style.get());
    129 }
    130 
    131 // FIXME: executeToggleStyleInList does not handle complicated cases such as <b><u>hello</u>world</b> properly.
    132 //        This function must use Editor::selectionHasStyle to determine the current style but we cannot fix this
    133 //        until https://bugs.webkit.org/show_bug.cgi?id=27818 is resolved.
    134 static bool executeToggleStyleInList(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValue* value)
    135 {
    136     RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(frame.selection().selection());
    137     if (!selectionStyle || !selectionStyle->style())
    138         return false;
    139 
    140     RefPtr<CSSValue> selectedCSSValue = selectionStyle->style()->getPropertyCSSValue(propertyID);
    141     String newStyle("none");
    142     if (selectedCSSValue->isValueList()) {
    143         RefPtr<CSSValueList> selectedCSSValueList = toCSSValueList(selectedCSSValue.get());
    144         if (!selectedCSSValueList->removeAll(value))
    145             selectedCSSValueList->append(value);
    146         if (selectedCSSValueList->length())
    147             newStyle = selectedCSSValueList->cssText();
    148 
    149     } else if (selectedCSSValue->cssText() == "none")
    150         newStyle = value->cssText();
    151 
    152     // FIXME: We shouldn't be having to convert new style into text.  We should have setPropertyCSSValue.
    153     RefPtr<MutableStylePropertySet> newMutableStyle = MutableStylePropertySet::create();
    154     newMutableStyle->setProperty(propertyID, newStyle);
    155     return applyCommandToFrame(frame, source, action, newMutableStyle.get());
    156 }
    157 
    158 static bool executeToggleStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const char* offValue, const char* onValue)
    159 {
    160     // Style is considered present when
    161     // Mac: present at the beginning of selection
    162     // other: present throughout the selection
    163 
    164     bool styleIsPresent;
    165     if (frame.editor().behavior().shouldToggleStyleBasedOnStartOfSelection())
    166         styleIsPresent = frame.editor().selectionStartHasStyle(propertyID, onValue);
    167     else
    168         styleIsPresent = frame.editor().selectionHasStyle(propertyID, onValue) == TrueTriState;
    169 
    170     RefPtr<EditingStyle> style = EditingStyle::create(propertyID, styleIsPresent ? offValue : onValue);
    171     return applyCommandToFrame(frame, source, action, style->style());
    172 }
    173 
    174 static bool executeApplyParagraphStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue)
    175 {
    176     RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
    177     style->setProperty(propertyID, propertyValue);
    178     // FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that?
    179     switch (source) {
    180     case CommandFromMenuOrKeyBinding:
    181         frame.editor().applyParagraphStyleToSelection(style.get(), action);
    182         return true;
    183     case CommandFromDOM:
    184     case CommandFromDOMWithUserInterface:
    185         frame.editor().applyParagraphStyle(style.get());
    186         return true;
    187     }
    188     ASSERT_NOT_REACHED();
    189     return false;
    190 }
    191 
    192 static bool executeInsertFragment(Frame& frame, PassRefPtr<DocumentFragment> fragment)
    193 {
    194     ASSERT(frame.document());
    195     ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionUnspecified)->apply();
    196     return true;
    197 }
    198 
    199 static bool executeInsertNode(Frame& frame, PassRefPtr<Node> content)
    200 {
    201     ASSERT(frame.document());
    202     RefPtr<DocumentFragment> fragment = DocumentFragment::create(*frame.document());
    203     TrackExceptionState exceptionState;
    204     fragment->appendChild(content, exceptionState);
    205     if (exceptionState.hadException())
    206         return false;
    207     return executeInsertFragment(frame, fragment.release());
    208 }
    209 
    210 static bool expandSelectionToGranularity(Frame& frame, TextGranularity granularity)
    211 {
    212     VisibleSelection selection = frame.selection().selection();
    213     selection.expandUsingGranularity(granularity);
    214     RefPtr<Range> newRange = selection.toNormalizedRange();
    215     if (!newRange)
    216         return false;
    217     if (newRange->collapsed(IGNORE_EXCEPTION))
    218         return false;
    219     RefPtr<Range> oldRange = frame.selection().selection().toNormalizedRange();
    220     EAffinity affinity = frame.selection().affinity();
    221     frame.selection().setSelectedRange(newRange.get(), affinity, true);
    222     return true;
    223 }
    224 
    225 static TriState stateStyle(Frame& frame, CSSPropertyID propertyID, const char* desiredValue)
    226 {
    227     if (frame.editor().behavior().shouldToggleStyleBasedOnStartOfSelection())
    228         return frame.editor().selectionStartHasStyle(propertyID, desiredValue) ? TrueTriState : FalseTriState;
    229     return frame.editor().selectionHasStyle(propertyID, desiredValue);
    230 }
    231 
    232 static String valueStyle(Frame& frame, CSSPropertyID propertyID)
    233 {
    234     // FIXME: Rather than retrieving the style at the start of the current selection,
    235     // we should retrieve the style present throughout the selection for non-Mac platforms.
    236     return frame.editor().selectionStartCSSPropertyValue(propertyID);
    237 }
    238 
    239 static TriState stateTextWritingDirection(Frame& frame, WritingDirection direction)
    240 {
    241     bool hasNestedOrMultipleEmbeddings;
    242     WritingDirection selectionDirection = EditingStyle::textDirectionForSelection(frame.selection().selection(),
    243         frame.selection().typingStyle(), hasNestedOrMultipleEmbeddings);
    244     // FXIME: We should be returning MixedTriState when selectionDirection == direction && hasNestedOrMultipleEmbeddings
    245     return (selectionDirection == direction && !hasNestedOrMultipleEmbeddings) ? TrueTriState : FalseTriState;
    246 }
    247 
    248 static unsigned verticalScrollDistance(Frame& frame)
    249 {
    250     Element* focusedElement = frame.document()->focusedElement();
    251     if (!focusedElement)
    252         return 0;
    253     RenderObject* renderer = focusedElement->renderer();
    254     if (!renderer || !renderer->isBox())
    255         return 0;
    256     RenderStyle* style = renderer->style();
    257     if (!style)
    258         return 0;
    259     if (!(style->overflowY() == OSCROLL || style->overflowY() == OAUTO || focusedElement->rendererIsEditable()))
    260         return 0;
    261     int height = std::min<int>(toRenderBox(renderer)->clientHeight(), frame.view()->visibleHeight());
    262     return static_cast<unsigned>(max(max<int>(height * ScrollableArea::minFractionToStepWhenPaging(), height - ScrollableArea::maxOverlapBetweenPages()), 1));
    263 }
    264 
    265 static RefPtr<Range> unionDOMRanges(Range* a, Range* b)
    266 {
    267     Range* start = a->compareBoundaryPoints(Range::START_TO_START, b, ASSERT_NO_EXCEPTION) <= 0 ? a : b;
    268     Range* end = a->compareBoundaryPoints(Range::END_TO_END, b, ASSERT_NO_EXCEPTION) <= 0 ? b : a;
    269 
    270     return Range::create(a->ownerDocument(), start->startContainer(), start->startOffset(), end->endContainer(), end->endOffset());
    271 }
    272 
    273 // Execute command functions
    274 
    275 static bool executeBackColor(Frame& frame, Event*, EditorCommandSource source, const String& value)
    276 {
    277     return executeApplyStyle(frame, source, EditActionSetBackgroundColor, CSSPropertyBackgroundColor, value);
    278 }
    279 
    280 static bool executeCopy(Frame& frame, Event*, EditorCommandSource, const String&)
    281 {
    282     frame.editor().copy();
    283     return true;
    284 }
    285 
    286 static bool executeCreateLink(Frame& frame, Event*, EditorCommandSource, const String& value)
    287 {
    288     // FIXME: If userInterface is true, we should display a dialog box to let the user enter a URL.
    289     if (value.isEmpty())
    290         return false;
    291     ASSERT(frame.document());
    292     CreateLinkCommand::create(*frame.document(), value)->apply();
    293     return true;
    294 }
    295 
    296 static bool executeCut(Frame& frame, Event*, EditorCommandSource, const String&)
    297 {
    298     frame.editor().cut();
    299     return true;
    300 }
    301 
    302 static bool executeDefaultParagraphSeparator(Frame& frame, Event*, EditorCommandSource, const String& value)
    303 {
    304     if (equalIgnoringCase(value, "div"))
    305         frame.editor().setDefaultParagraphSeparator(EditorParagraphSeparatorIsDiv);
    306     else if (equalIgnoringCase(value, "p"))
    307         frame.editor().setDefaultParagraphSeparator(EditorParagraphSeparatorIsP);
    308 
    309     return true;
    310 }
    311 
    312 static bool executeDelete(Frame& frame, Event*, EditorCommandSource source, const String&)
    313 {
    314     switch (source) {
    315     case CommandFromMenuOrKeyBinding: {
    316         // Doesn't modify the text if the current selection isn't a range.
    317         frame.editor().performDelete();
    318         return true;
    319     }
    320     case CommandFromDOM:
    321     case CommandFromDOMWithUserInterface:
    322         // If the current selection is a caret, delete the preceding character. IE performs forwardDelete, but we currently side with Firefox.
    323         // Doesn't scroll to make the selection visible, or modify the kill ring (this time, siding with IE, not Firefox).
    324         ASSERT(frame.document());
    325         TypingCommand::deleteKeyPressed(*frame.document(), frame.selection().granularity() == WordGranularity ? TypingCommand::SmartDelete : 0);
    326         return true;
    327     }
    328     ASSERT_NOT_REACHED();
    329     return false;
    330 }
    331 
    332 static bool executeDeleteBackward(Frame& frame, Event*, EditorCommandSource, const String&)
    333 {
    334     frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, false, true);
    335     return true;
    336 }
    337 
    338 static bool executeDeleteBackwardByDecomposingPreviousCharacter(Frame& frame, Event*, EditorCommandSource, const String&)
    339 {
    340     WTF_LOG_ERROR("DeleteBackwardByDecomposingPreviousCharacter is not implemented, doing DeleteBackward instead");
    341     frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, false, true);
    342     return true;
    343 }
    344 
    345 static bool executeDeleteForward(Frame& frame, Event*, EditorCommandSource, const String&)
    346 {
    347     frame.editor().deleteWithDirection(DirectionForward, CharacterGranularity, false, true);
    348     return true;
    349 }
    350 
    351 static bool executeDeleteToBeginningOfLine(Frame& frame, Event*, EditorCommandSource, const String&)
    352 {
    353     frame.editor().deleteWithDirection(DirectionBackward, LineBoundary, true, false);
    354     return true;
    355 }
    356 
    357 static bool executeDeleteToBeginningOfParagraph(Frame& frame, Event*, EditorCommandSource, const String&)
    358 {
    359     frame.editor().deleteWithDirection(DirectionBackward, ParagraphBoundary, true, false);
    360     return true;
    361 }
    362 
    363 static bool executeDeleteToEndOfLine(Frame& frame, Event*, EditorCommandSource, const String&)
    364 {
    365     // Despite its name, this command should delete the newline at the end of
    366     // a paragraph if you are at the end of a paragraph (like DeleteToEndOfParagraph).
    367     frame.editor().deleteWithDirection(DirectionForward, LineBoundary, true, false);
    368     return true;
    369 }
    370 
    371 static bool executeDeleteToEndOfParagraph(Frame& frame, Event*, EditorCommandSource, const String&)
    372 {
    373     // Despite its name, this command should delete the newline at the end of
    374     // a paragraph if you are at the end of a paragraph.
    375     frame.editor().deleteWithDirection(DirectionForward, ParagraphBoundary, true, false);
    376     return true;
    377 }
    378 
    379 static bool executeDeleteToMark(Frame& frame, Event*, EditorCommandSource, const String&)
    380 {
    381     RefPtr<Range> mark = frame.editor().mark().toNormalizedRange();
    382     if (mark) {
    383         bool selected = frame.selection().setSelectedRange(unionDOMRanges(mark.get(), frame.editor().selectedRange().get()).get(), DOWNSTREAM, true);
    384         ASSERT(selected);
    385         if (!selected)
    386             return false;
    387     }
    388     frame.editor().performDelete();
    389     frame.editor().setMark(frame.selection().selection());
    390     return true;
    391 }
    392 
    393 static bool executeDeleteWordBackward(Frame& frame, Event*, EditorCommandSource, const String&)
    394 {
    395     frame.editor().deleteWithDirection(DirectionBackward, WordGranularity, true, false);
    396     return true;
    397 }
    398 
    399 static bool executeDeleteWordForward(Frame& frame, Event*, EditorCommandSource, const String&)
    400 {
    401     frame.editor().deleteWithDirection(DirectionForward, WordGranularity, true, false);
    402     return true;
    403 }
    404 
    405 static bool executeFindString(Frame& frame, Event*, EditorCommandSource, const String& value)
    406 {
    407     return frame.editor().findString(value, true, false, true, false);
    408 }
    409 
    410 static bool executeFontName(Frame& frame, Event*, EditorCommandSource source, const String& value)
    411 {
    412     return executeApplyStyle(frame, source, EditActionSetFont, CSSPropertyFontFamily, value);
    413 }
    414 
    415 static bool executeFontSize(Frame& frame, Event*, EditorCommandSource source, const String& value)
    416 {
    417     CSSValueID size;
    418     if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size))
    419         return false;
    420     return executeApplyStyle(frame, source, EditActionChangeAttributes, CSSPropertyFontSize, size);
    421 }
    422 
    423 static bool executeFontSizeDelta(Frame& frame, Event*, EditorCommandSource source, const String& value)
    424 {
    425     return executeApplyStyle(frame, source, EditActionChangeAttributes, CSSPropertyWebkitFontSizeDelta, value);
    426 }
    427 
    428 static bool executeForeColor(Frame& frame, Event*, EditorCommandSource source, const String& value)
    429 {
    430     return executeApplyStyle(frame, source, EditActionSetColor, CSSPropertyColor, value);
    431 }
    432 
    433 static bool executeFormatBlock(Frame& frame, Event*, EditorCommandSource, const String& value)
    434 {
    435     String tagName = value.lower();
    436     if (tagName[0] == '<' && tagName[tagName.length() - 1] == '>')
    437         tagName = tagName.substring(1, tagName.length() - 2);
    438 
    439     AtomicString localName, prefix;
    440     if (!Document::parseQualifiedName(AtomicString(tagName), prefix, localName, IGNORE_EXCEPTION))
    441         return false;
    442     QualifiedName qualifiedTagName(prefix, localName, xhtmlNamespaceURI);
    443 
    444     ASSERT(frame.document());
    445     RefPtr<FormatBlockCommand> command = FormatBlockCommand::create(*frame.document(), qualifiedTagName);
    446     command->apply();
    447     return command->didApply();
    448 }
    449 
    450 static bool executeForwardDelete(Frame& frame, Event*, EditorCommandSource source, const String&)
    451 {
    452     switch (source) {
    453     case CommandFromMenuOrKeyBinding:
    454         frame.editor().deleteWithDirection(DirectionForward, CharacterGranularity, false, true);
    455         return true;
    456     case CommandFromDOM:
    457     case CommandFromDOMWithUserInterface:
    458         // Doesn't scroll to make the selection visible, or modify the kill ring.
    459         // ForwardDelete is not implemented in IE or Firefox, so this behavior is only needed for
    460         // backward compatibility with ourselves, and for consistency with Delete.
    461         ASSERT(frame.document());
    462         TypingCommand::forwardDeleteKeyPressed(*frame.document());
    463         return true;
    464     }
    465     ASSERT_NOT_REACHED();
    466     return false;
    467 }
    468 
    469 static bool executeIgnoreSpelling(Frame& frame, Event*, EditorCommandSource, const String&)
    470 {
    471     frame.spellChecker().ignoreSpelling();
    472     return true;
    473 }
    474 
    475 static bool executeIndent(Frame& frame, Event*, EditorCommandSource, const String&)
    476 {
    477     ASSERT(frame.document());
    478     IndentOutdentCommand::create(*frame.document(), IndentOutdentCommand::Indent)->apply();
    479     return true;
    480 }
    481 
    482 static bool executeInsertBacktab(Frame& frame, Event* event, EditorCommandSource, const String&)
    483 {
    484     return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\t", event, TextEventInputBackTab);
    485 }
    486 
    487 static bool executeInsertHorizontalRule(Frame& frame, Event*, EditorCommandSource, const String& value)
    488 {
    489     ASSERT(frame.document());
    490     RefPtr<HTMLHRElement> rule = HTMLHRElement::create(*frame.document());
    491     if (!value.isEmpty())
    492         rule->setIdAttribute(AtomicString(value));
    493     return executeInsertNode(frame, rule.release());
    494 }
    495 
    496 static bool executeInsertHTML(Frame& frame, Event*, EditorCommandSource, const String& value)
    497 {
    498     ASSERT(frame.document());
    499     return executeInsertFragment(frame, createFragmentFromMarkup(*frame.document(), value, ""));
    500 }
    501 
    502 static bool executeInsertImage(Frame& frame, Event*, EditorCommandSource, const String& value)
    503 {
    504     // FIXME: If userInterface is true, we should display a dialog box and let the user choose a local image.
    505     ASSERT(frame.document());
    506     RefPtr<HTMLImageElement> image = HTMLImageElement::create(*frame.document());
    507     image->setSrc(value);
    508     return executeInsertNode(frame, image.release());
    509 }
    510 
    511 static bool executeInsertLineBreak(Frame& frame, Event* event, EditorCommandSource source, const String&)
    512 {
    513     switch (source) {
    514     case CommandFromMenuOrKeyBinding:
    515         return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\n", event, TextEventInputLineBreak);
    516     case CommandFromDOM:
    517     case CommandFromDOMWithUserInterface:
    518         // Doesn't scroll to make the selection visible, or modify the kill ring.
    519         // InsertLineBreak is not implemented in IE or Firefox, so this behavior is only needed for
    520         // backward compatibility with ourselves, and for consistency with other commands.
    521         ASSERT(frame.document());
    522         TypingCommand::insertLineBreak(*frame.document(), 0);
    523         return true;
    524     }
    525     ASSERT_NOT_REACHED();
    526     return false;
    527 }
    528 
    529 static bool executeInsertNewline(Frame& frame, Event* event, EditorCommandSource, const String&)
    530 {
    531     Frame* targetFrame = WebCore::targetFrame(frame, event);
    532     return targetFrame->eventHandler().handleTextInputEvent("\n", event, targetFrame->editor().canEditRichly() ? TextEventInputKeyboard : TextEventInputLineBreak);
    533 }
    534 
    535 static bool executeInsertNewlineInQuotedContent(Frame& frame, Event*, EditorCommandSource, const String&)
    536 {
    537     ASSERT(frame.document());
    538     TypingCommand::insertParagraphSeparatorInQuotedContent(*frame.document());
    539     return true;
    540 }
    541 
    542 static bool executeInsertOrderedList(Frame& frame, Event*, EditorCommandSource, const String&)
    543 {
    544     ASSERT(frame.document());
    545     InsertListCommand::create(*frame.document(), InsertListCommand::OrderedList)->apply();
    546     return true;
    547 }
    548 
    549 static bool executeInsertParagraph(Frame& frame, Event*, EditorCommandSource, const String&)
    550 {
    551     ASSERT(frame.document());
    552     TypingCommand::insertParagraphSeparator(*frame.document(), 0);
    553     return true;
    554 }
    555 
    556 static bool executeInsertTab(Frame& frame, Event* event, EditorCommandSource, const String&)
    557 {
    558     return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\t", event);
    559 }
    560 
    561 static bool executeInsertText(Frame& frame, Event*, EditorCommandSource, const String& value)
    562 {
    563     ASSERT(frame.document());
    564     TypingCommand::insertText(*frame.document(), value, 0);
    565     return true;
    566 }
    567 
    568 static bool executeInsertUnorderedList(Frame& frame, Event*, EditorCommandSource, const String&)
    569 {
    570     ASSERT(frame.document());
    571     InsertListCommand::create(*frame.document(), InsertListCommand::UnorderedList)->apply();
    572     return true;
    573 }
    574 
    575 static bool executeJustifyCenter(Frame& frame, Event*, EditorCommandSource source, const String&)
    576 {
    577     return executeApplyParagraphStyle(frame, source, EditActionCenter, CSSPropertyTextAlign, "center");
    578 }
    579 
    580 static bool executeJustifyFull(Frame& frame, Event*, EditorCommandSource source, const String&)
    581 {
    582     return executeApplyParagraphStyle(frame, source, EditActionJustify, CSSPropertyTextAlign, "justify");
    583 }
    584 
    585 static bool executeJustifyLeft(Frame& frame, Event*, EditorCommandSource source, const String&)
    586 {
    587     return executeApplyParagraphStyle(frame, source, EditActionAlignLeft, CSSPropertyTextAlign, "left");
    588 }
    589 
    590 static bool executeJustifyRight(Frame& frame, Event*, EditorCommandSource source, const String&)
    591 {
    592     return executeApplyParagraphStyle(frame, source, EditActionAlignRight, CSSPropertyTextAlign, "right");
    593 }
    594 
    595 static bool executeMakeTextWritingDirectionLeftToRight(Frame& frame, Event*, EditorCommandSource, const String&)
    596 {
    597     RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
    598     style->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
    599     style->setProperty(CSSPropertyDirection, CSSValueLtr);
    600     frame.editor().applyStyle(style.get(), EditActionSetWritingDirection);
    601     return true;
    602 }
    603 
    604 static bool executeMakeTextWritingDirectionNatural(Frame& frame, Event*, EditorCommandSource, const String&)
    605 {
    606     RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
    607     style->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
    608     frame.editor().applyStyle(style.get(), EditActionSetWritingDirection);
    609     return true;
    610 }
    611 
    612 static bool executeMakeTextWritingDirectionRightToLeft(Frame& frame, Event*, EditorCommandSource, const String&)
    613 {
    614     RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
    615     style->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
    616     style->setProperty(CSSPropertyDirection, CSSValueRtl);
    617     frame.editor().applyStyle(style.get(), EditActionSetWritingDirection);
    618     return true;
    619 }
    620 
    621 static bool executeMoveBackward(Frame& frame, Event*, EditorCommandSource, const String&)
    622 {
    623     frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, CharacterGranularity, UserTriggered);
    624     return true;
    625 }
    626 
    627 static bool executeMoveBackwardAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    628 {
    629     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity, UserTriggered);
    630     return true;
    631 }
    632 
    633 static bool executeMoveDown(Frame& frame, Event*, EditorCommandSource, const String&)
    634 {
    635     return frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, LineGranularity, UserTriggered);
    636 }
    637 
    638 static bool executeMoveDownAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    639 {
    640     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, LineGranularity, UserTriggered);
    641     return true;
    642 }
    643 
    644 static bool executeMoveForward(Frame& frame, Event*, EditorCommandSource, const String&)
    645 {
    646     frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity, UserTriggered);
    647     return true;
    648 }
    649 
    650 static bool executeMoveForwardAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    651 {
    652     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity, UserTriggered);
    653     return true;
    654 }
    655 
    656 static bool executeMoveLeft(Frame& frame, Event*, EditorCommandSource, const String&)
    657 {
    658     return frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, CharacterGranularity, UserTriggered);
    659 }
    660 
    661 static bool executeMoveLeftAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    662 {
    663     frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, CharacterGranularity, UserTriggered);
    664     return true;
    665 }
    666 
    667 static bool executeMovePageDown(Frame& frame, Event*, EditorCommandSource, const String&)
    668 {
    669     unsigned distance = verticalScrollDistance(frame);
    670     if (!distance)
    671         return false;
    672     return frame.selection().modify(FrameSelection::AlterationMove, distance, FrameSelection::DirectionDown,
    673         UserTriggered, FrameSelection::AlignCursorOnScrollAlways);
    674 }
    675 
    676 static bool executeMovePageDownAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    677 {
    678     unsigned distance = verticalScrollDistance(frame);
    679     if (!distance)
    680         return false;
    681     return frame.selection().modify(FrameSelection::AlterationExtend, distance, FrameSelection::DirectionDown,
    682         UserTriggered, FrameSelection::AlignCursorOnScrollAlways);
    683 }
    684 
    685 static bool executeMovePageUp(Frame& frame, Event*, EditorCommandSource, const String&)
    686 {
    687     unsigned distance = verticalScrollDistance(frame);
    688     if (!distance)
    689         return false;
    690     return frame.selection().modify(FrameSelection::AlterationMove, distance, FrameSelection::DirectionUp,
    691         UserTriggered, FrameSelection::AlignCursorOnScrollAlways);
    692 }
    693 
    694 static bool executeMovePageUpAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    695 {
    696     unsigned distance = verticalScrollDistance(frame);
    697     if (!distance)
    698         return false;
    699     return frame.selection().modify(FrameSelection::AlterationExtend, distance, FrameSelection::DirectionUp,
    700         UserTriggered, FrameSelection::AlignCursorOnScrollAlways);
    701 }
    702 
    703 static bool executeMoveRight(Frame& frame, Event*, EditorCommandSource, const String&)
    704 {
    705     return frame.selection().modify(FrameSelection::AlterationMove, DirectionRight, CharacterGranularity, UserTriggered);
    706 }
    707 
    708 static bool executeMoveRightAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    709 {
    710     frame.selection().modify(FrameSelection::AlterationExtend, DirectionRight, CharacterGranularity, UserTriggered);
    711     return true;
    712 }
    713 
    714 static bool executeMoveToBeginningOfDocument(Frame& frame, Event*, EditorCommandSource, const String&)
    715 {
    716     frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, DocumentBoundary, UserTriggered);
    717     return true;
    718 }
    719 
    720 static bool executeMoveToBeginningOfDocumentAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    721 {
    722     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, DocumentBoundary, UserTriggered);
    723     return true;
    724 }
    725 
    726 static bool executeMoveToBeginningOfLine(Frame& frame, Event*, EditorCommandSource, const String&)
    727 {
    728     frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, LineBoundary, UserTriggered);
    729     return true;
    730 }
    731 
    732 static bool executeMoveToBeginningOfLineAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    733 {
    734     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, LineBoundary, UserTriggered);
    735     return true;
    736 }
    737 
    738 static bool executeMoveToBeginningOfParagraph(Frame& frame, Event*, EditorCommandSource, const String&)
    739 {
    740     frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, ParagraphBoundary, UserTriggered);
    741     return true;
    742 }
    743 
    744 static bool executeMoveToBeginningOfParagraphAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    745 {
    746     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, ParagraphBoundary, UserTriggered);
    747     return true;
    748 }
    749 
    750 static bool executeMoveToBeginningOfSentence(Frame& frame, Event*, EditorCommandSource, const String&)
    751 {
    752     frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, SentenceBoundary, UserTriggered);
    753     return true;
    754 }
    755 
    756 static bool executeMoveToBeginningOfSentenceAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    757 {
    758     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, SentenceBoundary, UserTriggered);
    759     return true;
    760 }
    761 
    762 static bool executeMoveToEndOfDocument(Frame& frame, Event*, EditorCommandSource, const String&)
    763 {
    764     frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, DocumentBoundary, UserTriggered);
    765     return true;
    766 }
    767 
    768 static bool executeMoveToEndOfDocumentAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    769 {
    770     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, DocumentBoundary, UserTriggered);
    771     return true;
    772 }
    773 
    774 static bool executeMoveToEndOfSentence(Frame& frame, Event*, EditorCommandSource, const String&)
    775 {
    776     frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, SentenceBoundary, UserTriggered);
    777     return true;
    778 }
    779 
    780 static bool executeMoveToEndOfSentenceAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    781 {
    782     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, SentenceBoundary, UserTriggered);
    783     return true;
    784 }
    785 
    786 static bool executeMoveToEndOfLine(Frame& frame, Event*, EditorCommandSource, const String&)
    787 {
    788     frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, LineBoundary, UserTriggered);
    789     return true;
    790 }
    791 
    792 static bool executeMoveToEndOfLineAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    793 {
    794     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, LineBoundary, UserTriggered);
    795     return true;
    796 }
    797 
    798 static bool executeMoveToEndOfParagraph(Frame& frame, Event*, EditorCommandSource, const String&)
    799 {
    800     frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, ParagraphBoundary, UserTriggered);
    801     return true;
    802 }
    803 
    804 static bool executeMoveToEndOfParagraphAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    805 {
    806     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, ParagraphBoundary, UserTriggered);
    807     return true;
    808 }
    809 
    810 static bool executeMoveParagraphBackward(Frame& frame, Event*, EditorCommandSource, const String&)
    811 {
    812     frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, ParagraphGranularity, UserTriggered);
    813     return true;
    814 }
    815 
    816 static bool executeMoveParagraphBackwardAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    817 {
    818     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, ParagraphGranularity, UserTriggered);
    819     return true;
    820 }
    821 
    822 static bool executeMoveParagraphForward(Frame& frame, Event*, EditorCommandSource, const String&)
    823 {
    824     frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, ParagraphGranularity, UserTriggered);
    825     return true;
    826 }
    827 
    828 static bool executeMoveParagraphForwardAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    829 {
    830     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, ParagraphGranularity, UserTriggered);
    831     return true;
    832 }
    833 
    834 static bool executeMoveUp(Frame& frame, Event*, EditorCommandSource, const String&)
    835 {
    836     return frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, LineGranularity, UserTriggered);
    837 }
    838 
    839 static bool executeMoveUpAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    840 {
    841     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, LineGranularity, UserTriggered);
    842     return true;
    843 }
    844 
    845 static bool executeMoveWordBackward(Frame& frame, Event*, EditorCommandSource, const String&)
    846 {
    847     frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, WordGranularity, UserTriggered);
    848     return true;
    849 }
    850 
    851 static bool executeMoveWordBackwardAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    852 {
    853     frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, WordGranularity, UserTriggered);
    854     return true;
    855 }
    856 
    857 static bool executeMoveWordForward(Frame& frame, Event*, EditorCommandSource, const String&)
    858 {
    859     frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, WordGranularity, UserTriggered);
    860     return true;
    861 }
    862 
    863 static bool executeMoveWordForwardAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    864 {
    865     frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, WordGranularity, UserTriggered);
    866     return true;
    867 }
    868 
    869 static bool executeMoveWordLeft(Frame& frame, Event*, EditorCommandSource, const String&)
    870 {
    871     frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, WordGranularity, UserTriggered);
    872     return true;
    873 }
    874 
    875 static bool executeMoveWordLeftAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    876 {
    877     frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, WordGranularity, UserTriggered);
    878     return true;
    879 }
    880 
    881 static bool executeMoveWordRight(Frame& frame, Event*, EditorCommandSource, const String&)
    882 {
    883     frame.selection().modify(FrameSelection::AlterationMove, DirectionRight, WordGranularity, UserTriggered);
    884     return true;
    885 }
    886 
    887 static bool executeMoveWordRightAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    888 {
    889     frame.selection().modify(FrameSelection::AlterationExtend, DirectionRight, WordGranularity, UserTriggered);
    890     return true;
    891 }
    892 
    893 static bool executeMoveToLeftEndOfLine(Frame& frame, Event*, EditorCommandSource, const String&)
    894 {
    895     frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, LineBoundary, UserTriggered);
    896     return true;
    897 }
    898 
    899 static bool executeMoveToLeftEndOfLineAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    900 {
    901     frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, LineBoundary, UserTriggered);
    902     return true;
    903 }
    904 
    905 static bool executeMoveToRightEndOfLine(Frame& frame, Event*, EditorCommandSource, const String&)
    906 {
    907     frame.selection().modify(FrameSelection::AlterationMove, DirectionRight, LineBoundary, UserTriggered);
    908     return true;
    909 }
    910 
    911 static bool executeMoveToRightEndOfLineAndModifySelection(Frame& frame, Event*, EditorCommandSource, const String&)
    912 {
    913     frame.selection().modify(FrameSelection::AlterationExtend, DirectionRight, LineBoundary, UserTriggered);
    914     return true;
    915 }
    916 
    917 static bool executeOutdent(Frame& frame, Event*, EditorCommandSource, const String&)
    918 {
    919     ASSERT(frame.document());
    920     IndentOutdentCommand::create(*frame.document(), IndentOutdentCommand::Outdent)->apply();
    921     return true;
    922 }
    923 
    924 static bool executeToggleOverwrite(Frame& frame, Event*, EditorCommandSource, const String&)
    925 {
    926     frame.editor().toggleOverwriteModeEnabled();
    927     return true;
    928 }
    929 
    930 static bool executePaste(Frame& frame, Event*, EditorCommandSource, const String&)
    931 {
    932     frame.editor().paste();
    933     return true;
    934 }
    935 
    936 static bool executePasteGlobalSelection(Frame& frame, Event*, EditorCommandSource source, const String&)
    937 {
    938     if (!frame.editor().behavior().supportsGlobalSelection())
    939         return false;
    940     ASSERT_UNUSED(source, source == CommandFromMenuOrKeyBinding);
    941 
    942     bool oldSelectionMode = Pasteboard::generalPasteboard()->isSelectionMode();
    943     Pasteboard::generalPasteboard()->setSelectionMode(true);
    944     frame.editor().paste();
    945     Pasteboard::generalPasteboard()->setSelectionMode(oldSelectionMode);
    946     return true;
    947 }
    948 
    949 static bool executePasteAndMatchStyle(Frame& frame, Event*, EditorCommandSource, const String&)
    950 {
    951     frame.editor().pasteAsPlainText();
    952     return true;
    953 }
    954 
    955 static bool executePrint(Frame& frame, Event*, EditorCommandSource, const String&)
    956 {
    957     Page* page = frame.page();
    958     if (!page)
    959         return false;
    960     page->chrome().print(&frame);
    961     return true;
    962 }
    963 
    964 static bool executeRedo(Frame& frame, Event*, EditorCommandSource, const String&)
    965 {
    966     frame.editor().redo();
    967     return true;
    968 }
    969 
    970 static bool executeRemoveFormat(Frame& frame, Event*, EditorCommandSource, const String&)
    971 {
    972     frame.editor().removeFormattingAndStyle();
    973     return true;
    974 }
    975 
    976 static bool executeScrollPageBackward(Frame& frame, Event*, EditorCommandSource, const String&)
    977 {
    978     return frame.eventHandler().scrollRecursively(ScrollBlockDirectionBackward, ScrollByPage);
    979 }
    980 
    981 static bool executeScrollPageForward(Frame& frame, Event*, EditorCommandSource, const String&)
    982 {
    983     return frame.eventHandler().scrollRecursively(ScrollBlockDirectionForward, ScrollByPage);
    984 }
    985 
    986 static bool executeScrollLineUp(Frame& frame, Event*, EditorCommandSource, const String&)
    987 {
    988     return frame.eventHandler().scrollRecursively(ScrollUp, ScrollByLine);
    989 }
    990 
    991 static bool executeScrollLineDown(Frame& frame, Event*, EditorCommandSource, const String&)
    992 {
    993     return frame.eventHandler().scrollRecursively(ScrollDown, ScrollByLine);
    994 }
    995 
    996 static bool executeScrollToBeginningOfDocument(Frame& frame, Event*, EditorCommandSource, const String&)
    997 {
    998     return frame.eventHandler().scrollRecursively(ScrollBlockDirectionBackward, ScrollByDocument);
    999 }
   1000 
   1001 static bool executeScrollToEndOfDocument(Frame& frame, Event*, EditorCommandSource, const String&)
   1002 {
   1003     return frame.eventHandler().scrollRecursively(ScrollBlockDirectionForward, ScrollByDocument);
   1004 }
   1005 
   1006 static bool executeSelectAll(Frame& frame, Event*, EditorCommandSource, const String&)
   1007 {
   1008     frame.selection().selectAll();
   1009     return true;
   1010 }
   1011 
   1012 static bool executeSelectLine(Frame& frame, Event*, EditorCommandSource, const String&)
   1013 {
   1014     return expandSelectionToGranularity(frame, LineGranularity);
   1015 }
   1016 
   1017 static bool executeSelectParagraph(Frame& frame, Event*, EditorCommandSource, const String&)
   1018 {
   1019     return expandSelectionToGranularity(frame, ParagraphGranularity);
   1020 }
   1021 
   1022 static bool executeSelectSentence(Frame& frame, Event*, EditorCommandSource, const String&)
   1023 {
   1024     return expandSelectionToGranularity(frame, SentenceGranularity);
   1025 }
   1026 
   1027 static bool executeSelectToMark(Frame& frame, Event*, EditorCommandSource, const String&)
   1028 {
   1029     RefPtr<Range> mark = frame.editor().mark().toNormalizedRange();
   1030     RefPtr<Range> selection = frame.editor().selectedRange();
   1031     if (!mark || !selection)
   1032         return false;
   1033     frame.selection().setSelectedRange(unionDOMRanges(mark.get(), selection.get()).get(), DOWNSTREAM, true);
   1034     return true;
   1035 }
   1036 
   1037 static bool executeSelectWord(Frame& frame, Event*, EditorCommandSource, const String&)
   1038 {
   1039     return expandSelectionToGranularity(frame, WordGranularity);
   1040 }
   1041 
   1042 static bool executeSetMark(Frame& frame, Event*, EditorCommandSource, const String&)
   1043 {
   1044     frame.editor().setMark(frame.selection().selection());
   1045     return true;
   1046 }
   1047 
   1048 static bool executeStrikethrough(Frame& frame, Event*, EditorCommandSource source, const String&)
   1049 {
   1050     RefPtr<CSSPrimitiveValue> lineThrough = CSSPrimitiveValue::createIdentifier(CSSValueLineThrough);
   1051     return executeToggleStyleInList(frame, source, EditActionUnderline, CSSPropertyWebkitTextDecorationsInEffect, lineThrough.get());
   1052 }
   1053 
   1054 static bool executeStyleWithCSS(Frame& frame, Event*, EditorCommandSource, const String& value)
   1055 {
   1056     frame.editor().setShouldStyleWithCSS(!equalIgnoringCase(value, "false"));
   1057     return true;
   1058 }
   1059 
   1060 static bool executeUseCSS(Frame& frame, Event*, EditorCommandSource, const String& value)
   1061 {
   1062     frame.editor().setShouldStyleWithCSS(equalIgnoringCase(value, "false"));
   1063     return true;
   1064 }
   1065 
   1066 static bool executeSubscript(Frame& frame, Event*, EditorCommandSource source, const String&)
   1067 {
   1068     return executeToggleStyle(frame, source, EditActionSubscript, CSSPropertyVerticalAlign, "baseline", "sub");
   1069 }
   1070 
   1071 static bool executeSuperscript(Frame& frame, Event*, EditorCommandSource source, const String&)
   1072 {
   1073     return executeToggleStyle(frame, source, EditActionSuperscript, CSSPropertyVerticalAlign, "baseline", "super");
   1074 }
   1075 
   1076 static bool executeSwapWithMark(Frame& frame, Event*, EditorCommandSource, const String&)
   1077 {
   1078     const VisibleSelection& mark = frame.editor().mark();
   1079     const VisibleSelection& selection = frame.selection().selection();
   1080     if (mark.isNone() || selection.isNone())
   1081         return false;
   1082     frame.selection().setSelection(mark);
   1083     frame.editor().setMark(selection);
   1084     return true;
   1085 }
   1086 
   1087 static bool executeToggleBold(Frame& frame, Event*, EditorCommandSource source, const String&)
   1088 {
   1089     return executeToggleStyle(frame, source, EditActionBold, CSSPropertyFontWeight, "normal", "bold");
   1090 }
   1091 
   1092 static bool executeToggleItalic(Frame& frame, Event*, EditorCommandSource source, const String&)
   1093 {
   1094     return executeToggleStyle(frame, source, EditActionItalics, CSSPropertyFontStyle, "normal", "italic");
   1095 }
   1096 
   1097 static bool executeTranspose(Frame& frame, Event*, EditorCommandSource, const String&)
   1098 {
   1099     frame.editor().transpose();
   1100     return true;
   1101 }
   1102 
   1103 static bool executeUnderline(Frame& frame, Event*, EditorCommandSource source, const String&)
   1104 {
   1105     RefPtr<CSSPrimitiveValue> underline = CSSPrimitiveValue::createIdentifier(CSSValueUnderline);
   1106     return executeToggleStyleInList(frame, source, EditActionUnderline, CSSPropertyWebkitTextDecorationsInEffect, underline.get());
   1107 }
   1108 
   1109 static bool executeUndo(Frame& frame, Event*, EditorCommandSource, const String&)
   1110 {
   1111     frame.editor().undo();
   1112     return true;
   1113 }
   1114 
   1115 static bool executeUnlink(Frame& frame, Event*, EditorCommandSource, const String&)
   1116 {
   1117     ASSERT(frame.document());
   1118     UnlinkCommand::create(*frame.document())->apply();
   1119     return true;
   1120 }
   1121 
   1122 static bool executeUnscript(Frame& frame, Event*, EditorCommandSource source, const String&)
   1123 {
   1124     return executeApplyStyle(frame, source, EditActionUnscript, CSSPropertyVerticalAlign, "baseline");
   1125 }
   1126 
   1127 static bool executeUnselect(Frame& frame, Event*, EditorCommandSource, const String&)
   1128 {
   1129     frame.selection().clear();
   1130     return true;
   1131 }
   1132 
   1133 static bool executeYank(Frame& frame, Event*, EditorCommandSource, const String&)
   1134 {
   1135     frame.editor().insertTextWithoutSendingTextEvent(frame.editor().killRing().yank(), false, 0);
   1136     frame.editor().killRing().setToYankedState();
   1137     return true;
   1138 }
   1139 
   1140 static bool executeYankAndSelect(Frame& frame, Event*, EditorCommandSource, const String&)
   1141 {
   1142     frame.editor().insertTextWithoutSendingTextEvent(frame.editor().killRing().yank(), true, 0);
   1143     frame.editor().killRing().setToYankedState();
   1144     return true;
   1145 }
   1146 
   1147 // Supported functions
   1148 
   1149 static bool supported(Frame*)
   1150 {
   1151     return true;
   1152 }
   1153 
   1154 static bool supportedFromMenuOrKeyBinding(Frame*)
   1155 {
   1156     return false;
   1157 }
   1158 
   1159 static bool supportedCopyCut(Frame* frame)
   1160 {
   1161     if (!frame)
   1162         return false;
   1163 
   1164     Settings* settings = frame->settings();
   1165     bool defaultValue = settings && settings->javaScriptCanAccessClipboard();
   1166     return frame->editor().client().canCopyCut(frame, defaultValue);
   1167 }
   1168 
   1169 static bool supportedPaste(Frame* frame)
   1170 {
   1171     if (!frame)
   1172         return false;
   1173 
   1174     Settings* settings = frame->settings();
   1175     bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->DOMPasteAllowed();
   1176     return frame->editor().client().canPaste(frame, defaultValue);
   1177 }
   1178 
   1179 // Enabled functions
   1180 
   1181 static bool enabled(Frame&, Event*, EditorCommandSource)
   1182 {
   1183     return true;
   1184 }
   1185 
   1186 static bool enabledVisibleSelection(Frame& frame, Event* event, EditorCommandSource)
   1187 {
   1188     // The term "visible" here includes a caret in editable text or a range in any text.
   1189     const VisibleSelection& selection = frame.editor().selectionForCommand(event);
   1190     return (selection.isCaret() && selection.isContentEditable()) || selection.isRange();
   1191 }
   1192 
   1193 static bool caretBrowsingEnabled(Frame& frame)
   1194 {
   1195     return frame.settings() && frame.settings()->caretBrowsingEnabled();
   1196 }
   1197 
   1198 static EditorCommandSource dummyEditorCommandSource = static_cast<EditorCommandSource>(0);
   1199 
   1200 static bool enabledVisibleSelectionOrCaretBrowsing(Frame& frame, Event* event, EditorCommandSource)
   1201 {
   1202     // The EditorCommandSource parameter is unused in enabledVisibleSelection, so just pass a dummy variable
   1203     return caretBrowsingEnabled(frame) || enabledVisibleSelection(frame, event, dummyEditorCommandSource);
   1204 }
   1205 
   1206 static bool enabledVisibleSelectionAndMark(Frame& frame, Event* event, EditorCommandSource)
   1207 {
   1208     const VisibleSelection& selection = frame.editor().selectionForCommand(event);
   1209     return ((selection.isCaret() && selection.isContentEditable()) || selection.isRange())
   1210         && frame.editor().mark().isCaretOrRange();
   1211 }
   1212 
   1213 static bool enableCaretInEditableText(Frame& frame, Event* event, EditorCommandSource)
   1214 {
   1215     const VisibleSelection& selection = frame.editor().selectionForCommand(event);
   1216     return selection.isCaret() && selection.isContentEditable();
   1217 }
   1218 
   1219 static bool enabledCopy(Frame& frame, Event*, EditorCommandSource)
   1220 {
   1221     return frame.editor().canDHTMLCopy() || frame.editor().canCopy();
   1222 }
   1223 
   1224 static bool enabledCut(Frame& frame, Event*, EditorCommandSource)
   1225 {
   1226     return frame.editor().canDHTMLCut() || frame.editor().canCut();
   1227 }
   1228 
   1229 static bool enabledInEditableText(Frame& frame, Event* event, EditorCommandSource)
   1230 {
   1231     return frame.editor().selectionForCommand(event).rootEditableElement();
   1232 }
   1233 
   1234 static bool enabledDelete(Frame& frame, Event* event, EditorCommandSource source)
   1235 {
   1236     switch (source) {
   1237     case CommandFromMenuOrKeyBinding:
   1238         return frame.editor().canDelete();
   1239     case CommandFromDOM:
   1240     case CommandFromDOMWithUserInterface:
   1241         // "Delete" from DOM is like delete/backspace keypress, affects selected range if non-empty,
   1242         // otherwise removes a character
   1243         return enabledInEditableText(frame, event, source);
   1244     }
   1245     ASSERT_NOT_REACHED();
   1246     return false;
   1247 }
   1248 
   1249 static bool enabledInEditableTextOrCaretBrowsing(Frame& frame, Event* event, EditorCommandSource)
   1250 {
   1251     // The EditorCommandSource parameter is unused in enabledInEditableText, so just pass a dummy variable
   1252     return caretBrowsingEnabled(frame) || enabledInEditableText(frame, event, dummyEditorCommandSource);
   1253 }
   1254 
   1255 static bool enabledInRichlyEditableText(Frame& frame, Event*, EditorCommandSource)
   1256 {
   1257     return frame.selection().isCaretOrRange() && frame.selection().isContentRichlyEditable() && frame.selection().rootEditableElement();
   1258 }
   1259 
   1260 static bool enabledPaste(Frame& frame, Event*, EditorCommandSource)
   1261 {
   1262     return frame.editor().canPaste();
   1263 }
   1264 
   1265 static bool enabledRangeInEditableText(Frame& frame, Event*, EditorCommandSource)
   1266 {
   1267     return frame.selection().isRange() && frame.selection().isContentEditable();
   1268 }
   1269 
   1270 static bool enabledRangeInRichlyEditableText(Frame& frame, Event*, EditorCommandSource)
   1271 {
   1272     return frame.selection().isRange() && frame.selection().isContentRichlyEditable();
   1273 }
   1274 
   1275 static bool enabledRedo(Frame& frame, Event*, EditorCommandSource)
   1276 {
   1277     return frame.editor().canRedo();
   1278 }
   1279 
   1280 static bool enabledUndo(Frame& frame, Event*, EditorCommandSource)
   1281 {
   1282     return frame.editor().canUndo();
   1283 }
   1284 
   1285 // State functions
   1286 
   1287 static TriState stateNone(Frame&, Event*)
   1288 {
   1289     return FalseTriState;
   1290 }
   1291 
   1292 static TriState stateBold(Frame& frame, Event*)
   1293 {
   1294     return stateStyle(frame, CSSPropertyFontWeight, "bold");
   1295 }
   1296 
   1297 static TriState stateItalic(Frame& frame, Event*)
   1298 {
   1299     return stateStyle(frame, CSSPropertyFontStyle, "italic");
   1300 }
   1301 
   1302 static TriState stateOrderedList(Frame& frame, Event*)
   1303 {
   1304     return frame.editor().selectionOrderedListState();
   1305 }
   1306 
   1307 static TriState stateStrikethrough(Frame& frame, Event*)
   1308 {
   1309     return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, "line-through");
   1310 }
   1311 
   1312 static TriState stateStyleWithCSS(Frame& frame, Event*)
   1313 {
   1314     return frame.editor().shouldStyleWithCSS() ? TrueTriState : FalseTriState;
   1315 }
   1316 
   1317 static TriState stateSubscript(Frame& frame, Event*)
   1318 {
   1319     return stateStyle(frame, CSSPropertyVerticalAlign, "sub");
   1320 }
   1321 
   1322 static TriState stateSuperscript(Frame& frame, Event*)
   1323 {
   1324     return stateStyle(frame, CSSPropertyVerticalAlign, "super");
   1325 }
   1326 
   1327 static TriState stateTextWritingDirectionLeftToRight(Frame& frame, Event*)
   1328 {
   1329     return stateTextWritingDirection(frame, LeftToRightWritingDirection);
   1330 }
   1331 
   1332 static TriState stateTextWritingDirectionNatural(Frame& frame, Event*)
   1333 {
   1334     return stateTextWritingDirection(frame, NaturalWritingDirection);
   1335 }
   1336 
   1337 static TriState stateTextWritingDirectionRightToLeft(Frame& frame, Event*)
   1338 {
   1339     return stateTextWritingDirection(frame, RightToLeftWritingDirection);
   1340 }
   1341 
   1342 static TriState stateUnderline(Frame& frame, Event*)
   1343 {
   1344     return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, "underline");
   1345 }
   1346 
   1347 static TriState stateUnorderedList(Frame& frame, Event*)
   1348 {
   1349     return frame.editor().selectionUnorderedListState();
   1350 }
   1351 
   1352 static TriState stateJustifyCenter(Frame& frame, Event*)
   1353 {
   1354     return stateStyle(frame, CSSPropertyTextAlign, "center");
   1355 }
   1356 
   1357 static TriState stateJustifyFull(Frame& frame, Event*)
   1358 {
   1359     return stateStyle(frame, CSSPropertyTextAlign, "justify");
   1360 }
   1361 
   1362 static TriState stateJustifyLeft(Frame& frame, Event*)
   1363 {
   1364     return stateStyle(frame, CSSPropertyTextAlign, "left");
   1365 }
   1366 
   1367 static TriState stateJustifyRight(Frame& frame, Event*)
   1368 {
   1369     return stateStyle(frame, CSSPropertyTextAlign, "right");
   1370 }
   1371 
   1372 // Value functions
   1373 
   1374 static String valueNull(Frame&, Event*)
   1375 {
   1376     return String();
   1377 }
   1378 
   1379 static String valueBackColor(Frame& frame, Event*)
   1380 {
   1381     return valueStyle(frame, CSSPropertyBackgroundColor);
   1382 }
   1383 
   1384 static String valueDefaultParagraphSeparator(Frame& frame, Event*)
   1385 {
   1386     switch (frame.editor().defaultParagraphSeparator()) {
   1387     case EditorParagraphSeparatorIsDiv:
   1388         return divTag.localName();
   1389     case EditorParagraphSeparatorIsP:
   1390         return pTag.localName();
   1391     }
   1392 
   1393     ASSERT_NOT_REACHED();
   1394     return String();
   1395 }
   1396 
   1397 static String valueFontName(Frame& frame, Event*)
   1398 {
   1399     return valueStyle(frame, CSSPropertyFontFamily);
   1400 }
   1401 
   1402 static String valueFontSize(Frame& frame, Event*)
   1403 {
   1404     return valueStyle(frame, CSSPropertyFontSize);
   1405 }
   1406 
   1407 static String valueFontSizeDelta(Frame& frame, Event*)
   1408 {
   1409     return valueStyle(frame, CSSPropertyWebkitFontSizeDelta);
   1410 }
   1411 
   1412 static String valueForeColor(Frame& frame, Event*)
   1413 {
   1414     return valueStyle(frame, CSSPropertyColor);
   1415 }
   1416 
   1417 static String valueFormatBlock(Frame& frame, Event*)
   1418 {
   1419     const VisibleSelection& selection = frame.selection().selection();
   1420     if (!selection.isNonOrphanedCaretOrRange() || !selection.isContentEditable())
   1421         return "";
   1422     Element* formatBlockElement = FormatBlockCommand::elementForFormatBlockCommand(selection.firstRange().get());
   1423     if (!formatBlockElement)
   1424         return "";
   1425     return formatBlockElement->localName();
   1426 }
   1427 
   1428 // Map of functions
   1429 
   1430 struct CommandEntry {
   1431     const char* name;
   1432     EditorInternalCommand command;
   1433 };
   1434 
   1435 static const CommandMap& createCommandMap()
   1436 {
   1437     static const CommandEntry commands[] = {
   1438         { "AlignCenter", { executeJustifyCenter, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1439         { "AlignJustified", { executeJustifyFull, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1440         { "AlignLeft", { executeJustifyLeft, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1441         { "AlignRight", { executeJustifyRight, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1442         { "BackColor", { executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueBackColor, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1443         { "BackwardDelete", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, // FIXME: remove BackwardDelete when Safari for Windows stops using it.
   1444         { "Bold", { executeToggleBold, supported, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1445         { "Copy", { executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
   1446         { "CreateLink", { executeCreateLink, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1447         { "Cut", { executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
   1448         { "DefaultParagraphSeparator", { executeDefaultParagraphSeparator, supported, enabled, stateNone, valueDefaultParagraphSeparator, notTextInsertion, doNotAllowExecutionWhenDisabled} },
   1449         { "Delete", { executeDelete, supported, enabledDelete, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1450         { "DeleteBackward", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1451         { "DeleteBackwardByDecomposingPreviousCharacter", { executeDeleteBackwardByDecomposingPreviousCharacter, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1452         { "DeleteForward", { executeDeleteForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1453         { "DeleteToBeginningOfLine", { executeDeleteToBeginningOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1454         { "DeleteToBeginningOfParagraph", { executeDeleteToBeginningOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1455         { "DeleteToEndOfLine", { executeDeleteToEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1456         { "DeleteToEndOfParagraph", { executeDeleteToEndOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1457         { "DeleteToMark", { executeDeleteToMark, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1458         { "DeleteWordBackward", { executeDeleteWordBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1459         { "DeleteWordForward", { executeDeleteWordForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1460         { "FindString", { executeFindString, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1461         { "FontName", { executeFontName, supported, enabledInEditableText, stateNone, valueFontName, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1462         { "FontSize", { executeFontSize, supported, enabledInEditableText, stateNone, valueFontSize, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1463         { "FontSizeDelta", { executeFontSizeDelta, supported, enabledInEditableText, stateNone, valueFontSizeDelta, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1464         { "ForeColor", { executeForeColor, supported, enabledInRichlyEditableText, stateNone, valueForeColor, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1465         { "FormatBlock", { executeFormatBlock, supported, enabledInRichlyEditableText, stateNone, valueFormatBlock, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1466         { "ForwardDelete", { executeForwardDelete, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1467         { "HiliteColor", { executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1468         { "IgnoreSpelling", { executeIgnoreSpelling, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1469         { "Indent", { executeIndent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1470         { "InsertBacktab", { executeInsertBacktab, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
   1471         { "InsertHTML", { executeInsertHTML, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1472         { "InsertHorizontalRule", { executeInsertHorizontalRule, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1473         { "InsertImage", { executeInsertImage, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1474         { "InsertLineBreak", { executeInsertLineBreak, supported, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
   1475         { "InsertNewline", { executeInsertNewline, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
   1476         { "InsertNewlineInQuotedContent", { executeInsertNewlineInQuotedContent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1477         { "InsertOrderedList", { executeInsertOrderedList, supported, enabledInRichlyEditableText, stateOrderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1478         { "InsertParagraph", { executeInsertParagraph, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1479         { "InsertTab", { executeInsertTab, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
   1480         { "InsertText", { executeInsertText, supported, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
   1481         { "InsertUnorderedList", { executeInsertUnorderedList, supported, enabledInRichlyEditableText, stateUnorderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1482         { "Italic", { executeToggleItalic, supported, enabledInRichlyEditableText, stateItalic, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1483         { "JustifyCenter", { executeJustifyCenter, supported, enabledInRichlyEditableText, stateJustifyCenter, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1484         { "JustifyFull", { executeJustifyFull, supported, enabledInRichlyEditableText, stateJustifyFull, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1485         { "JustifyLeft", { executeJustifyLeft, supported, enabledInRichlyEditableText, stateJustifyLeft, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1486         { "JustifyNone", { executeJustifyLeft, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1487         { "JustifyRight", { executeJustifyRight, supported, enabledInRichlyEditableText, stateJustifyRight, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1488         { "MakeTextWritingDirectionLeftToRight", { executeMakeTextWritingDirectionLeftToRight, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateTextWritingDirectionLeftToRight, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1489         { "MakeTextWritingDirectionNatural", { executeMakeTextWritingDirectionNatural, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateTextWritingDirectionNatural, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1490         { "MakeTextWritingDirectionRightToLeft", { executeMakeTextWritingDirectionRightToLeft, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateTextWritingDirectionRightToLeft, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1491         { "MoveBackward", { executeMoveBackward, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1492         { "MoveBackwardAndModifySelection", { executeMoveBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1493         { "MoveDown", { executeMoveDown, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1494         { "MoveDownAndModifySelection", { executeMoveDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1495         { "MoveForward", { executeMoveForward, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1496         { "MoveForwardAndModifySelection", { executeMoveForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1497         { "MoveLeft", { executeMoveLeft, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1498         { "MoveLeftAndModifySelection", { executeMoveLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1499         { "MovePageDown", { executeMovePageDown, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1500         { "MovePageDownAndModifySelection", { executeMovePageDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1501         { "MovePageUp", { executeMovePageUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1502         { "MovePageUpAndModifySelection", { executeMovePageUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1503         { "MoveParagraphBackward", { executeMoveParagraphBackward, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1504         { "MoveParagraphBackwardAndModifySelection", { executeMoveParagraphBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1505         { "MoveParagraphForward", { executeMoveParagraphForward, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1506         { "MoveParagraphForwardAndModifySelection", { executeMoveParagraphForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1507         { "MoveRight", { executeMoveRight, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1508         { "MoveRightAndModifySelection", { executeMoveRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1509         { "MoveToBeginningOfDocument", { executeMoveToBeginningOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1510         { "MoveToBeginningOfDocumentAndModifySelection", { executeMoveToBeginningOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1511         { "MoveToBeginningOfLine", { executeMoveToBeginningOfLine, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1512         { "MoveToBeginningOfLineAndModifySelection", { executeMoveToBeginningOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1513         { "MoveToBeginningOfParagraph", { executeMoveToBeginningOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1514         { "MoveToBeginningOfParagraphAndModifySelection", { executeMoveToBeginningOfParagraphAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1515         { "MoveToBeginningOfSentence", { executeMoveToBeginningOfSentence, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1516         { "MoveToBeginningOfSentenceAndModifySelection", { executeMoveToBeginningOfSentenceAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1517         { "MoveToEndOfDocument", { executeMoveToEndOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1518         { "MoveToEndOfDocumentAndModifySelection", { executeMoveToEndOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1519         { "MoveToEndOfLine", { executeMoveToEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1520         { "MoveToEndOfLineAndModifySelection", { executeMoveToEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1521         { "MoveToEndOfParagraph", { executeMoveToEndOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1522         { "MoveToEndOfParagraphAndModifySelection", { executeMoveToEndOfParagraphAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1523         { "MoveToEndOfSentence", { executeMoveToEndOfSentence, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1524         { "MoveToEndOfSentenceAndModifySelection", { executeMoveToEndOfSentenceAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1525         { "MoveToLeftEndOfLine", { executeMoveToLeftEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1526         { "MoveToLeftEndOfLineAndModifySelection", { executeMoveToLeftEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1527         { "MoveToRightEndOfLine", { executeMoveToRightEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1528         { "MoveToRightEndOfLineAndModifySelection", { executeMoveToRightEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1529         { "MoveUp", { executeMoveUp, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1530         { "MoveUpAndModifySelection", { executeMoveUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1531         { "MoveWordBackward", { executeMoveWordBackward, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1532         { "MoveWordBackwardAndModifySelection", { executeMoveWordBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1533         { "MoveWordForward", { executeMoveWordForward, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1534         { "MoveWordForwardAndModifySelection", { executeMoveWordForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1535         { "MoveWordLeft", { executeMoveWordLeft, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1536         { "MoveWordLeftAndModifySelection", { executeMoveWordLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1537         { "MoveWordRight", { executeMoveWordRight, supportedFromMenuOrKeyBinding, enabledInEditableTextOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1538         { "MoveWordRightAndModifySelection", { executeMoveWordRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelectionOrCaretBrowsing, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1539         { "Outdent", { executeOutdent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1540         { "OverWrite", { executeToggleOverwrite, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1541         { "Paste", { executePaste, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
   1542         { "PasteAndMatchStyle", { executePasteAndMatchStyle, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
   1543         { "PasteGlobalSelection", { executePasteGlobalSelection, supportedFromMenuOrKeyBinding, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
   1544         { "Print", { executePrint, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1545         { "Redo", { executeRedo, supported, enabledRedo, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1546         { "RemoveFormat", { executeRemoveFormat, supported, enabledRangeInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1547         { "ScrollPageBackward", { executeScrollPageBackward, supportedFromMenuOrKeyBinding, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1548         { "ScrollPageForward", { executeScrollPageForward, supportedFromMenuOrKeyBinding, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1549         { "ScrollLineUp", { executeScrollLineUp, supportedFromMenuOrKeyBinding, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1550         { "ScrollLineDown", { executeScrollLineDown, supportedFromMenuOrKeyBinding, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1551         { "ScrollToBeginningOfDocument", { executeScrollToBeginningOfDocument, supportedFromMenuOrKeyBinding, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1552         { "ScrollToEndOfDocument", { executeScrollToEndOfDocument, supportedFromMenuOrKeyBinding, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1553         { "SelectAll", { executeSelectAll, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1554         { "SelectLine", { executeSelectLine, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1555         { "SelectParagraph", { executeSelectParagraph, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1556         { "SelectSentence", { executeSelectSentence, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1557         { "SelectToMark", { executeSelectToMark, supportedFromMenuOrKeyBinding, enabledVisibleSelectionAndMark, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1558         { "SelectWord", { executeSelectWord, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1559         { "SetMark", { executeSetMark, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1560         { "Strikethrough", { executeStrikethrough, supported, enabledInRichlyEditableText, stateStrikethrough, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1561         { "StyleWithCSS", { executeStyleWithCSS, supported, enabled, stateStyleWithCSS, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1562         { "Subscript", { executeSubscript, supported, enabledInRichlyEditableText, stateSubscript, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1563         { "Superscript", { executeSuperscript, supported, enabledInRichlyEditableText, stateSuperscript, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1564         { "SwapWithMark", { executeSwapWithMark, supportedFromMenuOrKeyBinding, enabledVisibleSelectionAndMark, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1565         { "ToggleBold", { executeToggleBold, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1566         { "ToggleItalic", { executeToggleItalic, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateItalic, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1567         { "ToggleUnderline", { executeUnderline, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateUnderline, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1568         { "Transpose", { executeTranspose, supported, enableCaretInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1569         { "Underline", { executeUnderline, supported, enabledInRichlyEditableText, stateUnderline, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1570         { "Undo", { executeUndo, supported, enabledUndo, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1571         { "Unlink", { executeUnlink, supported, enabledRangeInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1572         { "Unscript", { executeUnscript, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1573         { "Unselect", { executeUnselect, supported, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1574         { "UseCSS", { executeUseCSS, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1575         { "Yank", { executeYank, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1576         { "YankAndSelect", { executeYankAndSelect, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
   1577     };
   1578 
   1579     // These unsupported commands are listed here since they appear in the Microsoft
   1580     // documentation used as the starting point for our DOM executeCommand support.
   1581     //
   1582     // 2D-Position (not supported)
   1583     // AbsolutePosition (not supported)
   1584     // BlockDirLTR (not supported)
   1585     // BlockDirRTL (not supported)
   1586     // BrowseMode (not supported)
   1587     // ClearAuthenticationCache (not supported)
   1588     // CreateBookmark (not supported)
   1589     // DirLTR (not supported)
   1590     // DirRTL (not supported)
   1591     // EditMode (not supported)
   1592     // InlineDirLTR (not supported)
   1593     // InlineDirRTL (not supported)
   1594     // InsertButton (not supported)
   1595     // InsertFieldSet (not supported)
   1596     // InsertIFrame (not supported)
   1597     // InsertInputButton (not supported)
   1598     // InsertInputCheckbox (not supported)
   1599     // InsertInputFileUpload (not supported)
   1600     // InsertInputHidden (not supported)
   1601     // InsertInputImage (not supported)
   1602     // InsertInputPassword (not supported)
   1603     // InsertInputRadio (not supported)
   1604     // InsertInputReset (not supported)
   1605     // InsertInputSubmit (not supported)
   1606     // InsertInputText (not supported)
   1607     // InsertMarquee (not supported)
   1608     // InsertSelectDropDown (not supported)
   1609     // InsertSelectListBox (not supported)
   1610     // InsertTextArea (not supported)
   1611     // LiveResize (not supported)
   1612     // MultipleSelection (not supported)
   1613     // Open (not supported)
   1614     // PlayImage (not supported)
   1615     // Refresh (not supported)
   1616     // RemoveParaFormat (not supported)
   1617     // SaveAs (not supported)
   1618     // SizeToControl (not supported)
   1619     // SizeToControlHeight (not supported)
   1620     // SizeToControlWidth (not supported)
   1621     // Stop (not supported)
   1622     // StopImage (not supported)
   1623     // Unbookmark (not supported)
   1624 
   1625     CommandMap& commandMap = *new CommandMap;
   1626 
   1627     for (size_t i = 0; i < WTF_ARRAY_LENGTH(commands); ++i) {
   1628         ASSERT(!commandMap.get(commands[i].name));
   1629         commandMap.set(commands[i].name, &commands[i].command);
   1630     }
   1631 
   1632     return commandMap;
   1633 }
   1634 
   1635 static const EditorInternalCommand* internalCommand(const String& commandName)
   1636 {
   1637     static const CommandMap& commandMap = createCommandMap();
   1638     return commandName.isEmpty() ? 0 : commandMap.get(commandName);
   1639 }
   1640 
   1641 Editor::Command Editor::command(const String& commandName)
   1642 {
   1643     return Command(internalCommand(commandName), CommandFromMenuOrKeyBinding, &m_frame);
   1644 }
   1645 
   1646 Editor::Command Editor::command(const String& commandName, EditorCommandSource source)
   1647 {
   1648     return Command(internalCommand(commandName), source, &m_frame);
   1649 }
   1650 
   1651 bool Editor::commandIsSupportedFromMenuOrKeyBinding(const String& commandName)
   1652 {
   1653     return internalCommand(commandName);
   1654 }
   1655 
   1656 Editor::Command::Command()
   1657     : m_command(0)
   1658 {
   1659 }
   1660 
   1661 Editor::Command::Command(const EditorInternalCommand* command, EditorCommandSource source, PassRefPtr<Frame> frame)
   1662     : m_command(command)
   1663     , m_source(source)
   1664     , m_frame(command ? frame : 0)
   1665 {
   1666     // Use separate assertions so we can tell which bad thing happened.
   1667     if (!command)
   1668         ASSERT(!m_frame);
   1669     else
   1670         ASSERT(m_frame);
   1671 }
   1672 
   1673 bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) const
   1674 {
   1675     if (!isEnabled(triggeringEvent)) {
   1676         // Let certain commands be executed when performed explicitly even if they are disabled.
   1677         if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled)
   1678             return false;
   1679     }
   1680     m_frame->document()->updateLayoutIgnorePendingStylesheets();
   1681     return m_command->execute(*m_frame, triggeringEvent, m_source, parameter);
   1682 }
   1683 
   1684 bool Editor::Command::execute(Event* triggeringEvent) const
   1685 {
   1686     return execute(String(), triggeringEvent);
   1687 }
   1688 
   1689 bool Editor::Command::isSupported() const
   1690 {
   1691     if (!m_command)
   1692         return false;
   1693     switch (m_source) {
   1694     case CommandFromMenuOrKeyBinding:
   1695         return true;
   1696     case CommandFromDOM:
   1697     case CommandFromDOMWithUserInterface:
   1698         return m_command->isSupportedFromDOM(m_frame.get());
   1699     }
   1700     ASSERT_NOT_REACHED();
   1701     return false;
   1702 }
   1703 
   1704 bool Editor::Command::isEnabled(Event* triggeringEvent) const
   1705 {
   1706     if (!isSupported() || !m_frame)
   1707         return false;
   1708     return m_command->isEnabled(*m_frame, triggeringEvent, m_source);
   1709 }
   1710 
   1711 TriState Editor::Command::state(Event* triggeringEvent) const
   1712 {
   1713     if (!isSupported() || !m_frame)
   1714         return FalseTriState;
   1715     return m_command->state(*m_frame, triggeringEvent);
   1716 }
   1717 
   1718 String Editor::Command::value(Event* triggeringEvent) const
   1719 {
   1720     if (!isSupported() || !m_frame)
   1721         return String();
   1722     if (m_command->value == valueNull && m_command->state != stateNone)
   1723         return m_command->state(*m_frame, triggeringEvent) == TrueTriState ? "true" : "false";
   1724     return m_command->value(*m_frame, triggeringEvent);
   1725 }
   1726 
   1727 bool Editor::Command::isTextInsertion() const
   1728 {
   1729     return m_command && m_command->isTextInsertion;
   1730 }
   1731 
   1732 } // namespace WebCore
   1733