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