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