summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/EditorCommands.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor/EditorCommands.cpp')
-rw-r--r--editor/libeditor/EditorCommands.cpp981
1 files changed, 981 insertions, 0 deletions
diff --git a/editor/libeditor/EditorCommands.cpp b/editor/libeditor/EditorCommands.cpp
new file mode 100644
index 0000000000..beb4f060ad
--- /dev/null
+++ b/editor/libeditor/EditorCommands.cpp
@@ -0,0 +1,981 @@
+/* -*- 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<nsIEditor> 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<nsIEditor> 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<nsIEditor> 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<bool> 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<nsresult> 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<nsITransferable> transferable;
+ if (params) {
+ nsCOMPtr<nsISupports> 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<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
+ if (editor) {
+ return GetCommandStateParams(GetInternalCommand(aCommandName),
+ MOZ_KnownLive(*aParams->AsCommandParams()),
+ MOZ_KnownLive(editor->AsEditorBase()),
+ nullptr);
+ }
+ nsCOMPtr<nsIEditingSession> 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> 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> 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> 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> 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> 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> 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> 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> 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<nsISupports> supports = aParams.GetISupports("transferable");
+ if (NS_WARN_IF(!supports)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsITransferable> 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> 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> 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> 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> 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<dom::Document> 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<nsISelectionController> 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> 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<nsresult> 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<nsresult> 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> 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> 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> 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