/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "EditorCommands.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/EditorBase.h" #include "mozilla/FlushType.h" #include "mozilla/HTMLEditor.h" #include "mozilla/Maybe.h" #include "mozilla/MozPromise.h" // for mozilla::detail::Any #include "mozilla/dom/Document.h" #include "mozilla/dom/Selection.h" #include "nsCommandParams.h" #include "nsIClipboard.h" #include "nsIEditingSession.h" #include "nsIPrincipal.h" #include "nsISelectionController.h" #include "nsITransferable.h" #include "nsString.h" #include "nsAString.h" class nsISupports; #define STATE_ENABLED "state_enabled" #define STATE_ATTRIBUTE "state_attribute" #define STATE_DATA "state_data" namespace mozilla { using detail::Any; /****************************************************************************** * mozilla::EditorCommand ******************************************************************************/ NS_IMPL_ISUPPORTS(EditorCommand, nsIControllerCommand) NS_IMETHODIMP EditorCommand::IsCommandEnabled(const char* aCommandName, nsISupports* aCommandRefCon, bool* aIsEnabled) { if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aIsEnabled)) { return NS_ERROR_INVALID_ARG; } nsCOMPtr editor = do_QueryInterface(aCommandRefCon); EditorBase* editorBase = editor ? editor->AsEditorBase() : nullptr; *aIsEnabled = IsCommandEnabled(GetInternalCommand(aCommandName), MOZ_KnownLive(editorBase)); return NS_OK; } NS_IMETHODIMP EditorCommand::DoCommand(const char* aCommandName, nsISupports* aCommandRefCon) { if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aCommandRefCon)) { return NS_ERROR_INVALID_ARG; } nsCOMPtr editor = do_QueryInterface(aCommandRefCon); if (NS_WARN_IF(!editor)) { return NS_ERROR_INVALID_ARG; } nsresult rv = DoCommand(GetInternalCommand(aCommandName), MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommand()"); return rv; } NS_IMETHODIMP EditorCommand::DoCommandParams(const char* aCommandName, nsICommandParams* aParams, nsISupports* aCommandRefCon) { if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aCommandRefCon)) { return NS_ERROR_INVALID_ARG; } nsCOMPtr editor = do_QueryInterface(aCommandRefCon); if (NS_WARN_IF(!editor)) { return NS_ERROR_INVALID_ARG; } nsCommandParams* params = aParams ? aParams->AsCommandParams() : nullptr; Command command = GetInternalCommand(aCommandName, params); EditorCommandParamType paramType = EditorCommand::GetParamType(command); if (paramType == EditorCommandParamType::None) { nsresult rv = DoCommandParam( command, MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommandParams()"); return rv; } if (Any(paramType & EditorCommandParamType::Bool)) { if (Any(paramType & EditorCommandParamType::StateAttribute)) { Maybe boolParam = Nothing(); if (params) { ErrorResult error; boolParam = Some(params->GetBool(STATE_ATTRIBUTE, error)); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } } nsresult rv = DoCommandParam( command, boolParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommandParams()"); return rv; } MOZ_ASSERT_UNREACHABLE("Unexpected state for bool"); return NS_ERROR_NOT_IMPLEMENTED; } // Special case for MultiStateCommandBase. It allows both CString and String // in STATE_ATTRIBUTE and CString is preferred. if (Any(paramType & EditorCommandParamType::CString) && Any(paramType & EditorCommandParamType::String)) { if (!params) { nsresult rv = DoCommandParam(command, VoidString(), MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to do command from " "nsIControllerCommand::DoCommandParams()"); return rv; } if (Any(paramType & EditorCommandParamType::StateAttribute)) { nsCString cStringParam; nsresult rv = params->GetCString(STATE_ATTRIBUTE, cStringParam); if (NS_SUCCEEDED(rv)) { NS_ConvertUTF8toUTF16 stringParam(cStringParam); nsresult rv = DoCommandParam(command, stringParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to do command from " "nsIControllerCommand::DoCommandParams()"); return rv; } nsString stringParam; DebugOnly rvIgnored = params->GetString(STATE_ATTRIBUTE, stringParam); NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "Failed to get string from STATE_ATTRIBUTE"); rv = DoCommandParam(command, stringParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to do command from " "nsIControllerCommand::DoCommandParams()"); return rv; } MOZ_ASSERT_UNREACHABLE("Unexpected state for CString/String"); return NS_ERROR_NOT_IMPLEMENTED; } if (Any(paramType & EditorCommandParamType::CString)) { if (!params) { nsresult rv = DoCommandParam(command, VoidCString(), MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommandParams()"); return rv; } if (Any(paramType & EditorCommandParamType::StateAttribute)) { nsCString cStringParam; nsresult rv = params->GetCString(STATE_ATTRIBUTE, cStringParam); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = DoCommandParam(command, cStringParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommandParams()"); return rv; } MOZ_ASSERT_UNREACHABLE("Unexpected state for CString"); return NS_ERROR_NOT_IMPLEMENTED; } if (Any(paramType & EditorCommandParamType::String)) { if (!params) { nsresult rv = DoCommandParam(command, VoidString(), MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommandParams()"); return rv; } nsString stringParam; if (Any(paramType & EditorCommandParamType::StateAttribute)) { nsresult rv = params->GetString(STATE_ATTRIBUTE, stringParam); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else if (Any(paramType & EditorCommandParamType::StateData)) { nsresult rv = params->GetString(STATE_DATA, stringParam); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else { MOZ_ASSERT_UNREACHABLE("Unexpected state for String"); return NS_ERROR_NOT_IMPLEMENTED; } nsresult rv = DoCommandParam( command, stringParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommandParams()"); return rv; } if (Any(paramType & EditorCommandParamType::Transferable)) { nsCOMPtr transferable; if (params) { nsCOMPtr supports = params->GetISupports("transferable"); transferable = do_QueryInterface(supports); } nsresult rv = DoCommandParam( command, transferable, MOZ_KnownLive(*editor->AsEditorBase()), nullptr); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "Failed to do command from nsIControllerCommand::DoCommandParams()"); return rv; } MOZ_ASSERT_UNREACHABLE("Unexpected param type"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP EditorCommand::GetCommandStateParams( const char* aCommandName, nsICommandParams* aParams, nsISupports* aCommandRefCon) { if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aParams)) { return NS_ERROR_INVALID_ARG; } nsCOMPtr editor = do_QueryInterface(aCommandRefCon); if (editor) { return GetCommandStateParams(GetInternalCommand(aCommandName), MOZ_KnownLive(*aParams->AsCommandParams()), MOZ_KnownLive(editor->AsEditorBase()), nullptr); } nsCOMPtr editingSession = do_QueryInterface(aCommandRefCon); if (editingSession) { return GetCommandStateParams(GetInternalCommand(aCommandName), MOZ_KnownLive(*aParams->AsCommandParams()), nullptr, editingSession); } return GetCommandStateParams(GetInternalCommand(aCommandName), MOZ_KnownLive(*aParams->AsCommandParams()), nullptr, nullptr); } /****************************************************************************** * mozilla::UndoCommand ******************************************************************************/ StaticRefPtr UndoCommand::sInstance; bool UndoCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable() && aEditorBase->CanUndo(); } nsresult UndoCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { nsresult rv = aEditorBase.UndoAsAction(1, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::UndoAsAction() failed"); return rv; } nsresult UndoCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::RedoCommand ******************************************************************************/ StaticRefPtr RedoCommand::sInstance; bool RedoCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable() && aEditorBase->CanRedo(); } nsresult RedoCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { nsresult rv = aEditorBase.RedoAsAction(1, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::RedoAsAction() failed"); return rv; } nsresult RedoCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::CutCommand ******************************************************************************/ StaticRefPtr CutCommand::sInstance; bool CutCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable() && aEditorBase->IsCutCommandEnabled(); } nsresult CutCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { nsresult rv = aEditorBase.CutAsAction(aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed"); return rv; } nsresult CutCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::CutOrDeleteCommand ******************************************************************************/ StaticRefPtr CutOrDeleteCommand::sInstance; bool CutOrDeleteCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable(); } nsresult CutOrDeleteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { dom::Selection* selection = aEditorBase.GetSelection(); if (selection && selection->IsCollapsed()) { nsresult rv = aEditorBase.DeleteSelectionAsAction( nsIEditor::eNext, nsIEditor::eStrip, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DeleteSelectionAsAction() failed"); return rv; } nsresult rv = aEditorBase.CutAsAction(aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed"); return rv; } nsresult CutOrDeleteCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::CopyCommand ******************************************************************************/ StaticRefPtr CopyCommand::sInstance; bool CopyCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsCopyCommandEnabled(); } nsresult CopyCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { // Shouldn't cause "beforeinput" event so that we don't need to specify // the given principal. return aEditorBase.Copy(); } nsresult CopyCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::CopyOrDeleteCommand ******************************************************************************/ StaticRefPtr CopyOrDeleteCommand::sInstance; bool CopyOrDeleteCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable(); } nsresult CopyOrDeleteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { dom::Selection* selection = aEditorBase.GetSelection(); if (selection && selection->IsCollapsed()) { nsresult rv = aEditorBase.DeleteSelectionAsAction( nsIEditor::eNextWord, nsIEditor::eStrip, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DeleteSelectionAsAction() failed"); return rv; } // Shouldn't cause "beforeinput" event so that we don't need to specify // the given principal. nsresult rv = aEditorBase.Copy(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::Copy() failed"); return rv; } nsresult CopyOrDeleteCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::PasteCommand ******************************************************************************/ StaticRefPtr PasteCommand::sInstance; bool PasteCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable() && aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard); } nsresult PasteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { nsresult rv = aEditorBase.PasteAsAction(nsIClipboard::kGlobalClipboard, EditorBase::DispatchPasteEvent::Yes, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::PasteAsAction(nsIClipboard::" "kGlobalClipboard, DispatchPasteEvent::Yes) failed"); return rv; } nsresult PasteCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::PasteTransferableCommand ******************************************************************************/ StaticRefPtr PasteTransferableCommand::sInstance; bool PasteTransferableCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable() && aEditorBase->CanPasteTransferable(nullptr); } nsresult PasteTransferableCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { return NS_ERROR_FAILURE; } nsresult PasteTransferableCommand::DoCommandParam( Command aCommand, nsITransferable* aTransferableParam, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { if (NS_WARN_IF(!aTransferableParam)) { return NS_ERROR_INVALID_ARG; } nsresult rv = aEditorBase.PasteTransferableAsAction( aTransferableParam, EditorBase::DispatchPasteEvent::Yes, aPrincipal); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "EditorBase::PasteTransferableAsAction(DispatchPasteEvent::Yes) failed"); return rv; } nsresult PasteTransferableCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { if (NS_WARN_IF(!aEditorBase)) { return NS_ERROR_INVALID_ARG; } nsCOMPtr supports = aParams.GetISupports("transferable"); if (NS_WARN_IF(!supports)) { return NS_ERROR_FAILURE; } nsCOMPtr trans; trans = do_QueryInterface(supports); if (NS_WARN_IF(!trans)) { return NS_ERROR_FAILURE; } return aParams.SetBool(STATE_ENABLED, aEditorBase->CanPasteTransferable(trans)); } /****************************************************************************** * mozilla::SwitchTextDirectionCommand ******************************************************************************/ StaticRefPtr SwitchTextDirectionCommand::sInstance; bool SwitchTextDirectionCommand::IsCommandEnabled( Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable(); } nsresult SwitchTextDirectionCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { nsresult rv = aEditorBase.ToggleTextDirectionAsAction(aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::ToggleTextDirectionAsAction() failed"); return rv; } nsresult SwitchTextDirectionCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::DeleteCommand ******************************************************************************/ StaticRefPtr DeleteCommand::sInstance; bool DeleteCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } // We can generally delete whenever the selection is editable. However, // cmd_delete doesn't make sense if the selection is collapsed because it's // directionless. bool isEnabled = aEditorBase->IsSelectionEditable(); if (aCommand == Command::Delete && isEnabled) { return aEditorBase->CanDeleteSelection(); } return isEnabled; } nsresult DeleteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { nsIEditor::EDirection deleteDir = nsIEditor::eNone; switch (aCommand) { case Command::Delete: // Really this should probably be eNone, but it only makes a difference // if the selection is collapsed, and then this command is disabled. So // let's keep it as it always was to avoid breaking things. deleteDir = nsIEditor::ePrevious; break; case Command::DeleteCharForward: deleteDir = nsIEditor::eNext; break; case Command::DeleteCharBackward: deleteDir = nsIEditor::ePrevious; break; case Command::DeleteWordBackward: deleteDir = nsIEditor::ePreviousWord; break; case Command::DeleteWordForward: deleteDir = nsIEditor::eNextWord; break; case Command::DeleteToBeginningOfLine: deleteDir = nsIEditor::eToBeginningOfLine; break; case Command::DeleteToEndOfLine: deleteDir = nsIEditor::eToEndOfLine; break; default: MOZ_CRASH("Unrecognized nsDeleteCommand"); } nsresult rv = aEditorBase.DeleteSelectionAsAction( deleteDir, nsIEditor::eStrip, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DeleteSelectionAsAction() failed"); return rv; } nsresult DeleteCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::SelectAllCommand ******************************************************************************/ StaticRefPtr SelectAllCommand::sInstance; bool SelectAllCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { // You can always select all, unless the selection is editable, // and the editable region is empty! if (!aEditorBase) { return true; } // You can select all if there is an editor which is non-empty return !aEditorBase->IsEmpty(); } nsresult SelectAllCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { // Shouldn't cause "beforeinput" event so that we don't need to specify // aPrincipal. nsresult rv = aEditorBase.SelectAll(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::SelectAll() failed"); return rv; } nsresult SelectAllCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::SelectionMoveCommands ******************************************************************************/ StaticRefPtr SelectionMoveCommands::sInstance; bool SelectionMoveCommands::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable(); } static const struct ScrollCommand { Command mReverseScroll; Command mForwardScroll; nsresult (NS_STDCALL nsISelectionController::*scroll)(bool); } scrollCommands[] = {{Command::ScrollTop, Command::ScrollBottom, &nsISelectionController::CompleteScroll}, {Command::ScrollPageUp, Command::ScrollPageDown, &nsISelectionController::ScrollPage}, {Command::ScrollLineUp, Command::ScrollLineDown, &nsISelectionController::ScrollLine}}; static const struct MoveCommand { Command mReverseMove; Command mForwardMove; Command mReverseSelect; Command mForwardSelect; nsresult (NS_STDCALL nsISelectionController::*move)(bool, bool); } moveCommands[] = { {Command::CharPrevious, Command::CharNext, Command::SelectCharPrevious, Command::SelectCharNext, &nsISelectionController::CharacterMove}, {Command::LinePrevious, Command::LineNext, Command::SelectLinePrevious, Command::SelectLineNext, &nsISelectionController::LineMove}, {Command::WordPrevious, Command::WordNext, Command::SelectWordPrevious, Command::SelectWordNext, &nsISelectionController::WordMove}, {Command::BeginLine, Command::EndLine, Command::SelectBeginLine, Command::SelectEndLine, &nsISelectionController::IntraLineMove}, {Command::MovePageUp, Command::MovePageDown, Command::SelectPageUp, Command::SelectPageDown, &nsISelectionController::PageMove}, {Command::MoveTop, Command::MoveBottom, Command::SelectTop, Command::SelectBottom, &nsISelectionController::CompleteMove}}; static const struct PhysicalCommand { Command mMove; Command mSelect; int16_t direction; int16_t amount; } physicalCommands[] = { {Command::MoveLeft, Command::SelectLeft, nsISelectionController::MOVE_LEFT, 0}, {Command::MoveRight, Command::SelectRight, nsISelectionController::MOVE_RIGHT, 0}, {Command::MoveUp, Command::SelectUp, nsISelectionController::MOVE_UP, 0}, {Command::MoveDown, Command::SelectDown, nsISelectionController::MOVE_DOWN, 0}, {Command::MoveLeft2, Command::SelectLeft2, nsISelectionController::MOVE_LEFT, 1}, {Command::MoveRight2, Command::SelectRight2, nsISelectionController::MOVE_RIGHT, 1}, {Command::MoveUp2, Command::SelectUp2, nsISelectionController::MOVE_UP, 1}, {Command::MoveDown2, Command::SelectDown2, nsISelectionController::MOVE_DOWN, 1}}; nsresult SelectionMoveCommands::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { RefPtr document = aEditorBase.GetDocument(); if (document) { // Most of the commands below (possibly all of them) need layout to // be up to date. document->FlushPendingNotifications(FlushType::Layout); } nsCOMPtr selectionController = aEditorBase.GetSelectionController(); if (NS_WARN_IF(!selectionController)) { return NS_ERROR_FAILURE; } // scroll commands for (size_t i = 0; i < ArrayLength(scrollCommands); i++) { const ScrollCommand& cmd = scrollCommands[i]; if (aCommand == cmd.mReverseScroll) { return (selectionController->*(cmd.scroll))(false); } if (aCommand == cmd.mForwardScroll) { return (selectionController->*(cmd.scroll))(true); } } // caret movement/selection commands for (size_t i = 0; i < ArrayLength(moveCommands); i++) { const MoveCommand& cmd = moveCommands[i]; if (aCommand == cmd.mReverseMove) { return (selectionController->*(cmd.move))(false, false); } if (aCommand == cmd.mForwardMove) { return (selectionController->*(cmd.move))(true, false); } if (aCommand == cmd.mReverseSelect) { return (selectionController->*(cmd.move))(false, true); } if (aCommand == cmd.mForwardSelect) { return (selectionController->*(cmd.move))(true, true); } } // physical-direction movement/selection for (size_t i = 0; i < ArrayLength(physicalCommands); i++) { const PhysicalCommand& cmd = physicalCommands[i]; if (aCommand == cmd.mMove) { nsresult rv = selectionController->PhysicalMove(cmd.direction, cmd.amount, false); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "nsISelectionController::PhysicalMove() failed to move caret"); return rv; } if (aCommand == cmd.mSelect) { nsresult rv = selectionController->PhysicalMove(cmd.direction, cmd.amount, true); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "nsISelectionController::PhysicalMove() failed to select"); return rv; } } return NS_ERROR_FAILURE; } nsresult SelectionMoveCommands::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::InsertPlaintextCommand ******************************************************************************/ StaticRefPtr InsertPlaintextCommand::sInstance; bool InsertPlaintextCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return aEditorBase->IsSelectionEditable(); } nsresult InsertPlaintextCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { // XXX InsertTextAsAction() is not same as OnInputText(). However, other // commands to insert line break or paragraph separator use OnInput*(). // According to the semantics of those methods, using *AsAction() is // better, however, this may not cause two or more placeholder // transactions to the top transaction since its name may not be // nsGkAtoms::TypingTxnName. DebugOnly rvIgnored = aEditorBase.InsertTextAsAction(u""_ns, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "EditorBase::InsertTextAsAction() failed, but ignored"); return NS_OK; } nsresult InsertPlaintextCommand::DoCommandParam( Command aCommand, const nsAString& aStringParam, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { if (NS_WARN_IF(aStringParam.IsVoid())) { return NS_ERROR_INVALID_ARG; } // XXX InsertTextAsAction() is not same as OnInputText(). However, other // commands to insert line break or paragraph separator use OnInput*(). // According to the semantics of those methods, using *AsAction() is // better, however, this may not cause two or more placeholder // transactions to the top transaction since its name may not be // nsGkAtoms::TypingTxnName. DebugOnly rvIgnored = aEditorBase.InsertTextAsAction(aStringParam, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "EditorBase::InsertTextAsAction() failed, but ignored"); return NS_OK; } nsresult InsertPlaintextCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::InsertParagraphCommand ******************************************************************************/ StaticRefPtr InsertParagraphCommand::sInstance; bool InsertParagraphCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase || aEditorBase->IsSingleLineEditor()) { return false; } return aEditorBase->IsSelectionEditable(); } nsresult InsertParagraphCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { if (aEditorBase.IsSingleLineEditor()) { return NS_ERROR_FAILURE; } if (aEditorBase.IsHTMLEditor()) { nsresult rv = MOZ_KnownLive(aEditorBase.AsHTMLEditor()) ->InsertParagraphSeparatorAsAction(aPrincipal); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "HTMLEditor::InsertParagraphSeparatorAsAction() failed"); return rv; } nsresult rv = aEditorBase.InsertLineBreakAsAction(aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::InsertLineBreakAsAction() failed"); return rv; } nsresult InsertParagraphCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::InsertLineBreakCommand ******************************************************************************/ StaticRefPtr InsertLineBreakCommand::sInstance; bool InsertLineBreakCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase || aEditorBase->IsSingleLineEditor()) { return false; } return aEditorBase->IsSelectionEditable(); } nsresult InsertLineBreakCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { if (aEditorBase.IsSingleLineEditor()) { return NS_ERROR_FAILURE; } nsresult rv = aEditorBase.InsertLineBreakAsAction(aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::InsertLineBreakAsAction() failed"); return rv; } nsresult InsertLineBreakCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { return aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); } /****************************************************************************** * mozilla::PasteQuotationCommand ******************************************************************************/ StaticRefPtr PasteQuotationCommand::sInstance; bool PasteQuotationCommand::IsCommandEnabled(Command aCommand, EditorBase* aEditorBase) const { if (!aEditorBase) { return false; } return !aEditorBase->IsSingleLineEditor() && aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard); } nsresult PasteQuotationCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const { nsresult rv = aEditorBase.PasteAsQuotationAsAction( nsIClipboard::kGlobalClipboard, EditorBase::DispatchPasteEvent::Yes, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::PasteAsQuotationAsAction(nsIClipboard::" "kGlobalClipboard, DispatchPasteEvent::Yes) failed"); return rv; } nsresult PasteQuotationCommand::GetCommandStateParams( Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, nsIEditingSession* aEditingSession) const { if (!aEditorBase) { return NS_OK; } aParams.SetBool(STATE_ENABLED, aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard)); return NS_OK; } } // namespace mozilla