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