/* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2009 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sky/engine/core/editing/Editor.h" #include "gen/sky/core/CSSPropertyNames.h" #include "gen/sky/core/CSSValueKeywords.h" #include "sky/engine/bindings/exception_state.h" #include "sky/engine/bindings/exception_state_placeholder.h" #include "sky/engine/core/css/CSSValueList.h" #include "sky/engine/core/css/StylePropertySet.h" #include "sky/engine/core/dom/DocumentFragment.h" #include "sky/engine/core/editing/FrameSelection.h" #include "sky/engine/core/editing/ReplaceSelectionCommand.h" #include "sky/engine/core/editing/SpellChecker.h" #include "sky/engine/core/editing/TypingCommand.h" #include "sky/engine/core/editing/htmlediting.h" #include "sky/engine/core/events/Event.h" #include "sky/engine/core/frame/FrameHost.h" #include "sky/engine/core/frame/FrameView.h" #include "sky/engine/core/frame/LocalFrame.h" #include "sky/engine/core/frame/Settings.h" #include "sky/engine/core/html/HTMLImageElement.h" #include "sky/engine/core/page/EditorClient.h" #include "sky/engine/core/page/EventHandler.h" #include "sky/engine/core/rendering/RenderBox.h" #include "sky/engine/public/platform/Platform.h" #include "sky/engine/wtf/text/AtomicString.h" namespace blink { class EditorInternalCommand { public: int idForUserMetrics; bool (*execute)(LocalFrame&, Event*, EditorCommandSource, const String&); bool (*isSupportedFromDOM)(LocalFrame*); bool (*isEnabled)(LocalFrame&, Event*, EditorCommandSource); TriState (*state)(LocalFrame&, Event*); String (*value)(LocalFrame&, Event*); bool isTextInsertion; bool allowExecutionWhenDisabled; }; typedef HashMap CommandMap; static const bool notTextInsertion = false; static const bool isTextInsertion = true; static const bool allowExecutionWhenDisabled = true; static const bool doNotAllowExecutionWhenDisabled = false; static const float kMinFractionToStepWhenPaging = 0.875f; // Related to Editor::selectionForCommand. // Certain operations continue to use the target control's selection even if the event handler // already moved the selection outside of the text control. static LocalFrame* targetFrame(LocalFrame& frame, Event* event) { if (!event) return &frame; Node* node = event->target()->toNode(); if (!node) return &frame; return node->document().frame(); } static unsigned verticalScrollDistance(LocalFrame& frame) { Element* focusedElement = frame.document()->focusedElement(); if (!focusedElement) return 0; RenderObject* renderer = focusedElement->renderer(); if (!renderer || !renderer->isBox()) return 0; RenderBox& renderBox = toRenderBox(*renderer); RenderStyle* style = renderBox.style(); if (!style) return 0; if (!focusedElement->hasEditableStyle()) return 0; int height = std::min(renderBox.clientHeight(), frame.view()->height()); return static_cast(max(height * kMinFractionToStepWhenPaging, 1)); } static bool executeCopy(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().copy(); return true; } static bool executeCut(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().cut(); return true; } static bool executeDeleteBackward(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, true); return true; } static bool executeDeleteForward(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().deleteWithDirection(DirectionForward, CharacterGranularity, true); return true; } static bool executeDeleteWordBackward(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().deleteWithDirection(DirectionBackward, WordGranularity, false); return true; } static bool executeDeleteWordForward(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().deleteWithDirection(DirectionForward, WordGranularity, false); return true; } static bool executeInsertNewline(LocalFrame& frame, Event* event, EditorCommandSource, const String&) { LocalFrame* targetFrame = blink::targetFrame(frame, event); return targetFrame->eventHandler().handleTextInputEvent("\n", event, targetFrame->editor().canEditRichly() ? TextEventInputKeyboard : TextEventInputLineBreak); } static bool executeMoveDown(LocalFrame& frame, Event*, EditorCommandSource, const String&) { return frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, LineGranularity, UserTriggered); } static bool executeMoveDownAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, LineGranularity, UserTriggered); return true; } static bool executeMoveLeft(LocalFrame& frame, Event*, EditorCommandSource, const String&) { return frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, CharacterGranularity, UserTriggered); } static bool executeMoveLeftAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, CharacterGranularity, UserTriggered); return true; } static bool executeMovePageDown(LocalFrame& frame, Event*, EditorCommandSource, const String&) { unsigned distance = verticalScrollDistance(frame); if (!distance) return false; return frame.selection().modify(FrameSelection::AlterationMove, distance, FrameSelection::DirectionDown, UserTriggered, FrameSelection::AlignCursorOnScrollAlways); } static bool executeMovePageDownAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { unsigned distance = verticalScrollDistance(frame); if (!distance) return false; return frame.selection().modify(FrameSelection::AlterationExtend, distance, FrameSelection::DirectionDown, UserTriggered, FrameSelection::AlignCursorOnScrollAlways); } static bool executeMovePageUp(LocalFrame& frame, Event*, EditorCommandSource, const String&) { unsigned distance = verticalScrollDistance(frame); if (!distance) return false; return frame.selection().modify(FrameSelection::AlterationMove, distance, FrameSelection::DirectionUp, UserTriggered, FrameSelection::AlignCursorOnScrollAlways); } static bool executeMovePageUpAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { unsigned distance = verticalScrollDistance(frame); if (!distance) return false; return frame.selection().modify(FrameSelection::AlterationExtend, distance, FrameSelection::DirectionUp, UserTriggered, FrameSelection::AlignCursorOnScrollAlways); } static bool executeMoveRight(LocalFrame& frame, Event*, EditorCommandSource, const String&) { return frame.selection().modify(FrameSelection::AlterationMove, DirectionRight, CharacterGranularity, UserTriggered); } static bool executeMoveRightAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionRight, CharacterGranularity, UserTriggered); return true; } static bool executeMoveToBeginningOfDocument(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, DocumentBoundary, UserTriggered); return true; } static bool executeMoveToBeginningOfDocumentAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, DocumentBoundary, UserTriggered); return true; } static bool executeMoveToBeginningOfLine(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, LineBoundary, UserTriggered); return true; } static bool executeMoveToBeginningOfLineAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, LineBoundary, UserTriggered); return true; } static bool executeMoveToEndOfDocument(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, DocumentBoundary, UserTriggered); return true; } static bool executeMoveToEndOfDocumentAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, DocumentBoundary, UserTriggered); return true; } static bool executeMoveToEndOfLine(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, LineBoundary, UserTriggered); return true; } static bool executeMoveToEndOfLineAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, LineBoundary, UserTriggered); return true; } static bool executeMoveParagraphBackward(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, ParagraphGranularity, UserTriggered); return true; } static bool executeMoveParagraphBackwardAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, ParagraphGranularity, UserTriggered); return true; } static bool executeMoveParagraphForward(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, ParagraphGranularity, UserTriggered); return true; } static bool executeMoveParagraphForwardAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionForward, ParagraphGranularity, UserTriggered); return true; } static bool executeMoveUp(LocalFrame& frame, Event*, EditorCommandSource, const String&) { return frame.selection().modify(FrameSelection::AlterationMove, DirectionBackward, LineGranularity, UserTriggered); } static bool executeMoveUpAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionBackward, LineGranularity, UserTriggered); return true; } static bool executeMoveWordLeft(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, WordGranularity, UserTriggered); return true; } static bool executeMoveWordLeftAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, WordGranularity, UserTriggered); return true; } static bool executeMoveWordRight(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionRight, WordGranularity, UserTriggered); return true; } static bool executeMoveWordRightAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionRight, WordGranularity, UserTriggered); return true; } static bool executeMoveToLeftEndOfLine(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationMove, DirectionLeft, LineBoundary, UserTriggered); return true; } static bool executeMoveToLeftEndOfLineAndModifySelection(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().modify(FrameSelection::AlterationExtend, DirectionLeft, LineBoundary, UserTriggered); return true; } static bool executeToggleOverwrite(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().toggleOverwriteModeEnabled(); return true; } static bool executePaste(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().paste(); return true; } static bool executePasteAndMatchStyle(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.editor().pasteAsPlainText(); return true; } static bool executeSelectAll(LocalFrame& frame, Event*, EditorCommandSource, const String&) { frame.selection().selectAll(); return true; } static bool supported(LocalFrame*) { return true; } static bool supportedFromMenuOrKeyBinding(LocalFrame*) { return false; } static bool supportedCopyCut(LocalFrame* frame) { if (!frame) return false; Settings* settings = frame->settings(); bool defaultValue = settings && settings->javaScriptCanAccessClipboard(); return frame->editor().client().canCopyCut(frame, defaultValue); } static bool supportedPaste(LocalFrame* frame) { if (!frame) return false; Settings* settings = frame->settings(); bool defaultValue = settings && settings->javaScriptCanAccessClipboard() && settings->DOMPasteAllowed(); return frame->editor().client().canPaste(frame, defaultValue); } // Enabled functions static bool enabled(LocalFrame&, Event*, EditorCommandSource) { return true; } static bool enabledVisibleSelection(LocalFrame& frame, Event* event, EditorCommandSource) { // The term "visible" here includes a caret in editable text or a range in any text. const VisibleSelection& selection = frame.editor().selectionForCommand(event); return (selection.isCaret() && selection.isContentEditable()) || selection.isRange(); } static bool enabledCopy(LocalFrame& frame, Event*, EditorCommandSource) { return frame.editor().canDHTMLCopy() || frame.editor().canCopy(); } static bool enabledCut(LocalFrame& frame, Event*, EditorCommandSource) { return frame.editor().canDHTMLCut() || frame.editor().canCut(); } static bool enabledInEditableText(LocalFrame& frame, Event* event, EditorCommandSource) { return frame.editor().selectionForCommand(event).rootEditableElement(); } static bool enabledInRichlyEditableText(LocalFrame& frame, Event*, EditorCommandSource) { return frame.selection().isCaretOrRange() && frame.selection().isContentRichlyEditable() && frame.selection().rootEditableElement(); } static bool enabledPaste(LocalFrame& frame, Event*, EditorCommandSource) { return frame.editor().canPaste(); } static TriState stateNone(LocalFrame&, Event*) { return FalseTriState; } static String valueNull(LocalFrame&, Event*) { return String(); } // Map of functions struct CommandEntry { const char* name; EditorInternalCommand command; }; static const CommandMap& createCommandMap() { // If you add new commands, you should assign new Id to each idForUserMetrics and update MappedEditingCommands // in chrome/trunk/src/tools/metrics/histograms/histograms.xml. static const CommandEntry commands[] = { { "Copy", {7, executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "Cut", {9, executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "DeleteBackward", {12, executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "DeleteForward", {14, executeDeleteForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "DeleteWordBackward", {20, executeDeleteWordBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "DeleteWordForward", {21, executeDeleteWordForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "InsertNewline", {37, executeInsertNewline, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveDown", {55, executeMoveDown, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveDownAndModifySelection", {56, executeMoveDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveLeft", {59, executeMoveLeft, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveLeftAndModifySelection", {60, executeMoveLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MovePageDown", {61, executeMovePageDown, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MovePageDownAndModifySelection", {62, executeMovePageDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MovePageUp", {63, executeMovePageUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MovePageUpAndModifySelection", {64, executeMovePageUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveParagraphBackward", {65, executeMoveParagraphBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveParagraphBackwardAndModifySelection", {66, executeMoveParagraphBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveParagraphForward", {67, executeMoveParagraphForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveParagraphForwardAndModifySelection", {68, executeMoveParagraphForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveRight", {69, executeMoveRight, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveRightAndModifySelection", {70, executeMoveRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToBeginningOfDocument", {71, executeMoveToBeginningOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToBeginningOfDocumentAndModifySelection", {72, executeMoveToBeginningOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToBeginningOfLine", {73, executeMoveToBeginningOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToBeginningOfLineAndModifySelection", {74, executeMoveToBeginningOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToEndOfDocument", {79, executeMoveToEndOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToEndOfDocumentAndModifySelection", {80, executeMoveToEndOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToEndOfLine", {81, executeMoveToEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToEndOfLineAndModifySelection", {82, executeMoveToEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToLeftEndOfLine", {87, executeMoveToLeftEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveToLeftEndOfLineAndModifySelection", {88, executeMoveToLeftEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveUp", {91, executeMoveUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveUpAndModifySelection", {92, executeMoveUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveWordLeft", {97, executeMoveWordLeft, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveWordLeftAndModifySelection", {98, executeMoveWordLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveWordRight", {99, executeMoveWordRight, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "MoveWordRightAndModifySelection", {100, executeMoveWordRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "OverWrite", {102, executeToggleOverwrite, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, { "Paste", {103, executePaste, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "PasteAndMatchStyle", {104, executePasteAndMatchStyle, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, { "SelectAll", {115, executeSelectAll, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, }; CommandMap& commandMap = *new CommandMap; #if ENABLE(ASSERT) HashSet idSet; #endif for (size_t i = 0; i < WTF_ARRAY_LENGTH(commands); ++i) { const CommandEntry& command = commands[i]; ASSERT(!commandMap.get(command.name)); commandMap.set(command.name, &command.command); #if ENABLE(ASSERT) ASSERT(!idSet.contains(command.command.idForUserMetrics)); idSet.add(command.command.idForUserMetrics); #endif } return commandMap; } static const EditorInternalCommand* internalCommand(const String& commandName) { static const CommandMap& commandMap = createCommandMap(); return commandName.isEmpty() ? 0 : commandMap.get(commandName); } Editor::Command Editor::command(const String& commandName) { return Command(internalCommand(commandName), CommandFromMenuOrKeyBinding, &m_frame); } Editor::Command Editor::command(const String& commandName, EditorCommandSource source) { return Command(internalCommand(commandName), source, &m_frame); } bool Editor::executeCommand(const String& commandName) { // Specially handling commands that Editor::execCommand does not directly // support. if (commandName == "DeleteToEndOfParagraph") { if (!deleteWithDirection(DirectionForward, ParagraphBoundary, false)) deleteWithDirection(DirectionForward, CharacterGranularity, false); return true; } if (commandName == "DeleteBackward") return command(AtomicString("BackwardDelete")).execute(); if (commandName == "DeleteForward") return command(AtomicString("ForwardDelete")).execute(); if (commandName == "AdvanceToNextMisspelling") { // Wee need to pass false here or else the currently selected word will never be skipped. spellChecker().advanceToNextMisspelling(false); return true; } if (commandName == "ToggleSpellPanel") { spellChecker().showSpellingGuessPanel(); return true; } return command(commandName).execute(); } bool Editor::executeCommand(const String& commandName, const String& value) { if (commandName == "showGuessPanel") { spellChecker().showSpellingGuessPanel(); return true; } return command(commandName).execute(value); } Editor::Command::Command() : m_command(0) { } Editor::Command::Command(const EditorInternalCommand* command, EditorCommandSource source, PassRefPtr frame) : m_command(command) , m_source(source) , m_frame(command ? frame : nullptr) { // Use separate assertions so we can tell which bad thing happened. if (!command) ASSERT(!m_frame); else ASSERT(m_frame); } bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) const { if (!isEnabled(triggeringEvent)) { // Let certain commands be executed when performed explicitly even if they are disabled. if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled) return false; } m_frame->document()->updateLayout(); blink::Platform::current()->histogramSparse("WebCore.Editing.Commands", m_command->idForUserMetrics); return m_command->execute(*m_frame, triggeringEvent, m_source, parameter); } bool Editor::Command::execute(Event* triggeringEvent) const { return execute(String(), triggeringEvent); } bool Editor::Command::isSupported() const { if (!m_command) return false; switch (m_source) { case CommandFromMenuOrKeyBinding: return true; case CommandFromDOM: case CommandFromDOMWithUserInterface: return m_command->isSupportedFromDOM(m_frame.get()); } ASSERT_NOT_REACHED(); return false; } bool Editor::Command::isEnabled(Event* triggeringEvent) const { if (!isSupported() || !m_frame) return false; return m_command->isEnabled(*m_frame, triggeringEvent, m_source); } TriState Editor::Command::state(Event* triggeringEvent) const { if (!isSupported() || !m_frame) return FalseTriState; return m_command->state(*m_frame, triggeringEvent); } String Editor::Command::value(Event* triggeringEvent) const { if (!isSupported() || !m_frame) return String(); if (m_command->value == valueNull && m_command->state != stateNone) return m_command->state(*m_frame, triggeringEvent) == TrueTriState ? "true" : "false"; return m_command->value(*m_frame, triggeringEvent); } bool Editor::Command::isTextInsertion() const { return m_command && m_command->isTextInsertion; } int Editor::Command::idForHistogram() const { return isSupported() ? m_command->idForUserMetrics : 0; } } // namespace blink