Bug 1669129 - [devtools] Enable devtools.overflow.debugging.enabled. r=jdescottes
[gecko.git] / editor / libeditor / EditorCommands.cpp
blobfc14dc6a9f3ea9d4d21fe9f8c6477b18a15648db
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "EditorCommands.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/FlushType.h"
11 #include "mozilla/HTMLEditor.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/MozPromise.h" // for mozilla::detail::Any
14 #include "mozilla/TextEditor.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/Selection.h"
17 #include "nsCommandParams.h"
18 #include "nsIClipboard.h"
19 #include "nsIEditingSession.h"
20 #include "nsIPrincipal.h"
21 #include "nsISelectionController.h"
22 #include "nsITransferable.h"
23 #include "nsString.h"
24 #include "nsAString.h"
26 class nsISupports;
28 #define STATE_ENABLED "state_enabled"
29 #define STATE_ATTRIBUTE "state_attribute"
30 #define STATE_DATA "state_data"
32 namespace mozilla {
34 using detail::Any;
36 /******************************************************************************
37 * mozilla::EditorCommand
38 ******************************************************************************/
40 NS_IMPL_ISUPPORTS(EditorCommand, nsIControllerCommand)
42 NS_IMETHODIMP EditorCommand::IsCommandEnabled(const char* aCommandName,
43 nsISupports* aCommandRefCon,
44 bool* aIsEnabled) {
45 if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aIsEnabled)) {
46 return NS_ERROR_INVALID_ARG;
49 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
50 TextEditor* textEditor = editor ? editor->AsTextEditor() : nullptr;
51 *aIsEnabled = IsCommandEnabled(GetInternalCommand(aCommandName),
52 MOZ_KnownLive(textEditor));
53 return NS_OK;
56 NS_IMETHODIMP EditorCommand::DoCommand(const char* aCommandName,
57 nsISupports* aCommandRefCon) {
58 if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aCommandRefCon)) {
59 return NS_ERROR_INVALID_ARG;
61 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
62 if (NS_WARN_IF(!editor)) {
63 return NS_ERROR_INVALID_ARG;
65 nsresult rv = DoCommand(GetInternalCommand(aCommandName),
66 MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
67 NS_WARNING_ASSERTION(
68 NS_SUCCEEDED(rv),
69 "Failed to do command from nsIControllerCommand::DoCommand()");
70 return rv;
73 NS_IMETHODIMP EditorCommand::DoCommandParams(const char* aCommandName,
74 nsICommandParams* aParams,
75 nsISupports* aCommandRefCon) {
76 if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aCommandRefCon)) {
77 return NS_ERROR_INVALID_ARG;
79 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
80 if (NS_WARN_IF(!editor)) {
81 return NS_ERROR_INVALID_ARG;
83 nsCommandParams* params = aParams ? aParams->AsCommandParams() : nullptr;
84 Command command = GetInternalCommand(aCommandName, params);
85 EditorCommandParamType paramType = EditorCommand::GetParamType(command);
86 if (paramType == EditorCommandParamType::None) {
87 nsresult rv = DoCommandParam(
88 command, MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
89 NS_WARNING_ASSERTION(
90 NS_SUCCEEDED(rv),
91 "Failed to do command from nsIControllerCommand::DoCommandParams()");
92 return rv;
95 if (Any(paramType & EditorCommandParamType::Bool)) {
96 if (Any(paramType & EditorCommandParamType::StateAttribute)) {
97 Maybe<bool> boolParam = Nothing();
98 if (params) {
99 ErrorResult error;
100 boolParam = Some(params->GetBool(STATE_ATTRIBUTE, error));
101 if (NS_WARN_IF(error.Failed())) {
102 return error.StealNSResult();
105 nsresult rv = DoCommandParam(
106 command, boolParam, MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
107 NS_WARNING_ASSERTION(
108 NS_SUCCEEDED(rv),
109 "Failed to do command from nsIControllerCommand::DoCommandParams()");
110 return rv;
112 MOZ_ASSERT_UNREACHABLE("Unexpected state for bool");
113 return NS_ERROR_NOT_IMPLEMENTED;
116 // Special case for MultiStateCommandBase. It allows both CString and String
117 // in STATE_ATTRIBUTE and CString is preferred.
118 if (Any(paramType & EditorCommandParamType::CString) &&
119 Any(paramType & EditorCommandParamType::String)) {
120 if (!params) {
121 nsresult rv =
122 DoCommandParam(command, VoidString(),
123 MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
124 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
125 "Failed to do command from "
126 "nsIControllerCommand::DoCommandParams()");
127 return rv;
129 if (Any(paramType & EditorCommandParamType::StateAttribute)) {
130 nsCString cStringParam;
131 nsresult rv = params->GetCString(STATE_ATTRIBUTE, cStringParam);
132 if (NS_SUCCEEDED(rv)) {
133 NS_ConvertUTF8toUTF16 stringParam(cStringParam);
134 nsresult rv =
135 DoCommandParam(command, stringParam,
136 MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
137 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
138 "Failed to do command from "
139 "nsIControllerCommand::DoCommandParams()");
140 return rv;
142 nsString stringParam;
143 DebugOnly<nsresult> rvIgnored =
144 params->GetString(STATE_ATTRIBUTE, stringParam);
145 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
146 "Failed to get string from STATE_ATTRIBUTE");
147 rv = DoCommandParam(command, stringParam,
148 MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
149 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
150 "Failed to do command from "
151 "nsIControllerCommand::DoCommandParams()");
152 return rv;
154 MOZ_ASSERT_UNREACHABLE("Unexpected state for CString/String");
155 return NS_ERROR_NOT_IMPLEMENTED;
158 if (Any(paramType & EditorCommandParamType::CString)) {
159 if (!params) {
160 nsresult rv =
161 DoCommandParam(command, VoidCString(),
162 MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
163 NS_WARNING_ASSERTION(
164 NS_SUCCEEDED(rv),
165 "Failed to do command from nsIControllerCommand::DoCommandParams()");
166 return rv;
168 if (Any(paramType & EditorCommandParamType::StateAttribute)) {
169 nsCString cStringParam;
170 nsresult rv = params->GetCString(STATE_ATTRIBUTE, cStringParam);
171 if (NS_WARN_IF(NS_FAILED(rv))) {
172 return rv;
174 rv = DoCommandParam(command, cStringParam,
175 MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
176 NS_WARNING_ASSERTION(
177 NS_SUCCEEDED(rv),
178 "Failed to do command from nsIControllerCommand::DoCommandParams()");
179 return rv;
181 MOZ_ASSERT_UNREACHABLE("Unexpected state for CString");
182 return NS_ERROR_NOT_IMPLEMENTED;
185 if (Any(paramType & EditorCommandParamType::String)) {
186 if (!params) {
187 nsresult rv =
188 DoCommandParam(command, VoidString(),
189 MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
190 NS_WARNING_ASSERTION(
191 NS_SUCCEEDED(rv),
192 "Failed to do command from nsIControllerCommand::DoCommandParams()");
193 return rv;
195 nsString stringParam;
196 if (Any(paramType & EditorCommandParamType::StateAttribute)) {
197 nsresult rv = params->GetString(STATE_ATTRIBUTE, stringParam);
198 if (NS_WARN_IF(NS_FAILED(rv))) {
199 return rv;
201 } else if (Any(paramType & EditorCommandParamType::StateData)) {
202 nsresult rv = params->GetString(STATE_DATA, stringParam);
203 if (NS_WARN_IF(NS_FAILED(rv))) {
204 return rv;
206 } else {
207 MOZ_ASSERT_UNREACHABLE("Unexpected state for String");
208 return NS_ERROR_NOT_IMPLEMENTED;
210 nsresult rv = DoCommandParam(
211 command, stringParam, MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
212 NS_WARNING_ASSERTION(
213 NS_SUCCEEDED(rv),
214 "Failed to do command from nsIControllerCommand::DoCommandParams()");
215 return rv;
218 if (Any(paramType & EditorCommandParamType::Transferable)) {
219 nsCOMPtr<nsITransferable> transferable;
220 if (params) {
221 nsCOMPtr<nsISupports> supports = params->GetISupports("transferable");
222 transferable = do_QueryInterface(supports);
224 nsresult rv = DoCommandParam(
225 command, transferable, MOZ_KnownLive(*editor->AsTextEditor()), nullptr);
226 NS_WARNING_ASSERTION(
227 NS_SUCCEEDED(rv),
228 "Failed to do command from nsIControllerCommand::DoCommandParams()");
229 return rv;
232 MOZ_ASSERT_UNREACHABLE("Unexpected param type");
233 return NS_ERROR_NOT_IMPLEMENTED;
236 NS_IMETHODIMP EditorCommand::GetCommandStateParams(
237 const char* aCommandName, nsICommandParams* aParams,
238 nsISupports* aCommandRefCon) {
239 if (NS_WARN_IF(!aCommandName) || NS_WARN_IF(!aParams)) {
240 return NS_ERROR_INVALID_ARG;
242 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
243 if (editor) {
244 return GetCommandStateParams(GetInternalCommand(aCommandName),
245 MOZ_KnownLive(*aParams->AsCommandParams()),
246 MOZ_KnownLive(editor->AsTextEditor()),
247 nullptr);
249 nsCOMPtr<nsIEditingSession> editingSession =
250 do_QueryInterface(aCommandRefCon);
251 if (editingSession) {
252 return GetCommandStateParams(GetInternalCommand(aCommandName),
253 MOZ_KnownLive(*aParams->AsCommandParams()),
254 nullptr, editingSession);
256 return GetCommandStateParams(GetInternalCommand(aCommandName),
257 MOZ_KnownLive(*aParams->AsCommandParams()),
258 nullptr, nullptr);
261 /******************************************************************************
262 * mozilla::UndoCommand
263 ******************************************************************************/
265 StaticRefPtr<UndoCommand> UndoCommand::sInstance;
267 bool UndoCommand::IsCommandEnabled(Command aCommand,
268 TextEditor* aTextEditor) const {
269 if (!aTextEditor) {
270 return false;
272 return aTextEditor->IsSelectionEditable() && aTextEditor->CanUndo();
275 nsresult UndoCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
276 nsIPrincipal* aPrincipal) const {
277 nsresult rv = aTextEditor.UndoAsAction(1, aPrincipal);
278 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::UndoAsAction() failed");
279 return rv;
282 nsresult UndoCommand::GetCommandStateParams(
283 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
284 nsIEditingSession* aEditingSession) const {
285 return aParams.SetBool(STATE_ENABLED,
286 IsCommandEnabled(aCommand, aTextEditor));
289 /******************************************************************************
290 * mozilla::RedoCommand
291 ******************************************************************************/
293 StaticRefPtr<RedoCommand> RedoCommand::sInstance;
295 bool RedoCommand::IsCommandEnabled(Command aCommand,
296 TextEditor* aTextEditor) const {
297 if (!aTextEditor) {
298 return false;
300 return aTextEditor->IsSelectionEditable() && aTextEditor->CanRedo();
303 nsresult RedoCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
304 nsIPrincipal* aPrincipal) const {
305 nsresult rv = aTextEditor.RedoAsAction(1, aPrincipal);
306 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::RedoAsAction() failed");
307 return rv;
310 nsresult RedoCommand::GetCommandStateParams(
311 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
312 nsIEditingSession* aEditingSession) const {
313 return aParams.SetBool(STATE_ENABLED,
314 IsCommandEnabled(aCommand, aTextEditor));
317 /******************************************************************************
318 * mozilla::CutCommand
319 ******************************************************************************/
321 StaticRefPtr<CutCommand> CutCommand::sInstance;
323 bool CutCommand::IsCommandEnabled(Command aCommand,
324 TextEditor* aTextEditor) const {
325 if (!aTextEditor) {
326 return false;
328 return aTextEditor->IsSelectionEditable() &&
329 aTextEditor->IsCutCommandEnabled();
332 nsresult CutCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
333 nsIPrincipal* aPrincipal) const {
334 nsresult rv = aTextEditor.CutAsAction(aPrincipal);
335 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::CutAsAction() failed");
336 return rv;
339 nsresult CutCommand::GetCommandStateParams(
340 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
341 nsIEditingSession* aEditingSession) const {
342 return aParams.SetBool(STATE_ENABLED,
343 IsCommandEnabled(aCommand, aTextEditor));
346 /******************************************************************************
347 * mozilla::CutOrDeleteCommand
348 ******************************************************************************/
350 StaticRefPtr<CutOrDeleteCommand> CutOrDeleteCommand::sInstance;
352 bool CutOrDeleteCommand::IsCommandEnabled(Command aCommand,
353 TextEditor* aTextEditor) const {
354 if (!aTextEditor) {
355 return false;
357 return aTextEditor->IsSelectionEditable();
360 nsresult CutOrDeleteCommand::DoCommand(Command aCommand,
361 TextEditor& aTextEditor,
362 nsIPrincipal* aPrincipal) const {
363 dom::Selection* selection = aTextEditor.GetSelection();
364 if (selection && selection->IsCollapsed()) {
365 nsresult rv = aTextEditor.DeleteSelectionAsAction(
366 nsIEditor::eNext, nsIEditor::eStrip, aPrincipal);
367 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
368 "EditorBase::DeleteSelectionAsAction() failed");
369 return rv;
371 nsresult rv = aTextEditor.CutAsAction(aPrincipal);
372 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::CutAsAction() failed");
373 return rv;
376 nsresult CutOrDeleteCommand::GetCommandStateParams(
377 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
378 nsIEditingSession* aEditingSession) const {
379 return aParams.SetBool(STATE_ENABLED,
380 IsCommandEnabled(aCommand, aTextEditor));
383 /******************************************************************************
384 * mozilla::CopyCommand
385 ******************************************************************************/
387 StaticRefPtr<CopyCommand> CopyCommand::sInstance;
389 bool CopyCommand::IsCommandEnabled(Command aCommand,
390 TextEditor* aTextEditor) const {
391 if (!aTextEditor) {
392 return false;
394 return aTextEditor->IsCopyCommandEnabled();
397 nsresult CopyCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
398 nsIPrincipal* aPrincipal) const {
399 // Shouldn't cause "beforeinput" event so that we don't need to specify
400 // the given principal.
401 return aTextEditor.Copy();
404 nsresult CopyCommand::GetCommandStateParams(
405 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
406 nsIEditingSession* aEditingSession) const {
407 return aParams.SetBool(STATE_ENABLED,
408 IsCommandEnabled(aCommand, aTextEditor));
411 /******************************************************************************
412 * mozilla::CopyOrDeleteCommand
413 ******************************************************************************/
415 StaticRefPtr<CopyOrDeleteCommand> CopyOrDeleteCommand::sInstance;
417 bool CopyOrDeleteCommand::IsCommandEnabled(Command aCommand,
418 TextEditor* aTextEditor) const {
419 if (!aTextEditor) {
420 return false;
422 return aTextEditor->IsSelectionEditable();
425 nsresult CopyOrDeleteCommand::DoCommand(Command aCommand,
426 TextEditor& aTextEditor,
427 nsIPrincipal* aPrincipal) const {
428 dom::Selection* selection = aTextEditor.GetSelection();
429 if (selection && selection->IsCollapsed()) {
430 nsresult rv = aTextEditor.DeleteSelectionAsAction(
431 nsIEditor::eNextWord, nsIEditor::eStrip, aPrincipal);
432 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
433 "EditorBase::DeleteSelectionAsAction() failed");
434 return rv;
436 // Shouldn't cause "beforeinput" event so that we don't need to specify
437 // the given principal.
438 nsresult rv = aTextEditor.Copy();
439 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::Copy() failed");
440 return rv;
443 nsresult CopyOrDeleteCommand::GetCommandStateParams(
444 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
445 nsIEditingSession* aEditingSession) const {
446 return aParams.SetBool(STATE_ENABLED,
447 IsCommandEnabled(aCommand, aTextEditor));
450 /******************************************************************************
451 * mozilla::PasteCommand
452 ******************************************************************************/
454 StaticRefPtr<PasteCommand> PasteCommand::sInstance;
456 bool PasteCommand::IsCommandEnabled(Command aCommand,
457 TextEditor* aTextEditor) const {
458 if (!aTextEditor) {
459 return false;
461 return aTextEditor->IsSelectionEditable() &&
462 aTextEditor->CanPaste(nsIClipboard::kGlobalClipboard);
465 nsresult PasteCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
466 nsIPrincipal* aPrincipal) const {
467 nsresult rv = aTextEditor.PasteAsAction(nsIClipboard::kGlobalClipboard, true,
468 aPrincipal);
469 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::PasteAsAction() failed");
470 return rv;
473 nsresult PasteCommand::GetCommandStateParams(
474 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
475 nsIEditingSession* aEditingSession) const {
476 return aParams.SetBool(STATE_ENABLED,
477 IsCommandEnabled(aCommand, aTextEditor));
480 /******************************************************************************
481 * mozilla::PasteTransferableCommand
482 ******************************************************************************/
484 StaticRefPtr<PasteTransferableCommand> PasteTransferableCommand::sInstance;
486 bool PasteTransferableCommand::IsCommandEnabled(Command aCommand,
487 TextEditor* aTextEditor) const {
488 if (!aTextEditor) {
489 return false;
491 return aTextEditor->IsSelectionEditable() &&
492 aTextEditor->CanPasteTransferable(nullptr);
495 nsresult PasteTransferableCommand::DoCommand(Command aCommand,
496 TextEditor& aTextEditor,
497 nsIPrincipal* aPrincipal) const {
498 return NS_ERROR_FAILURE;
501 nsresult PasteTransferableCommand::DoCommandParam(
502 Command aCommand, nsITransferable* aTransferableParam,
503 TextEditor& aTextEditor, nsIPrincipal* aPrincipal) const {
504 if (NS_WARN_IF(!aTransferableParam)) {
505 return NS_ERROR_INVALID_ARG;
507 nsresult rv =
508 aTextEditor.PasteTransferableAsAction(aTransferableParam, aPrincipal);
509 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
510 "TextEditor::PasteTransferableAsAction() failed");
511 return rv;
514 nsresult PasteTransferableCommand::GetCommandStateParams(
515 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
516 nsIEditingSession* aEditingSession) const {
517 if (NS_WARN_IF(!aTextEditor)) {
518 return NS_ERROR_INVALID_ARG;
521 nsCOMPtr<nsISupports> supports = aParams.GetISupports("transferable");
522 if (NS_WARN_IF(!supports)) {
523 return NS_ERROR_FAILURE;
526 nsCOMPtr<nsITransferable> trans;
527 trans = do_QueryInterface(supports);
528 if (NS_WARN_IF(!trans)) {
529 return NS_ERROR_FAILURE;
532 return aParams.SetBool(STATE_ENABLED,
533 aTextEditor->CanPasteTransferable(trans));
536 /******************************************************************************
537 * mozilla::SwitchTextDirectionCommand
538 ******************************************************************************/
540 StaticRefPtr<SwitchTextDirectionCommand> SwitchTextDirectionCommand::sInstance;
542 bool SwitchTextDirectionCommand::IsCommandEnabled(
543 Command aCommand, TextEditor* aTextEditor) const {
544 if (!aTextEditor) {
545 return false;
547 return aTextEditor->IsSelectionEditable();
550 nsresult SwitchTextDirectionCommand::DoCommand(Command aCommand,
551 TextEditor& aTextEditor,
552 nsIPrincipal* aPrincipal) const {
553 nsresult rv = aTextEditor.ToggleTextDirectionAsAction(aPrincipal);
554 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
555 "EditorBase::ToggleTextDirectionAsAction() failed");
556 return rv;
559 nsresult SwitchTextDirectionCommand::GetCommandStateParams(
560 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
561 nsIEditingSession* aEditingSession) const {
562 return aParams.SetBool(STATE_ENABLED,
563 IsCommandEnabled(aCommand, aTextEditor));
566 /******************************************************************************
567 * mozilla::DeleteCommand
568 ******************************************************************************/
570 StaticRefPtr<DeleteCommand> DeleteCommand::sInstance;
572 bool DeleteCommand::IsCommandEnabled(Command aCommand,
573 TextEditor* aTextEditor) const {
574 if (!aTextEditor) {
575 return false;
577 // We can generally delete whenever the selection is editable. However,
578 // cmd_delete doesn't make sense if the selection is collapsed because it's
579 // directionless.
580 bool isEnabled = aTextEditor->IsSelectionEditable();
582 if (aCommand == Command::Delete && isEnabled) {
583 return aTextEditor->CanDeleteSelection();
585 return isEnabled;
588 nsresult DeleteCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
589 nsIPrincipal* aPrincipal) const {
590 nsIEditor::EDirection deleteDir = nsIEditor::eNone;
591 switch (aCommand) {
592 case Command::Delete:
593 // Really this should probably be eNone, but it only makes a difference
594 // if the selection is collapsed, and then this command is disabled. So
595 // let's keep it as it always was to avoid breaking things.
596 deleteDir = nsIEditor::ePrevious;
597 break;
598 case Command::DeleteCharForward:
599 deleteDir = nsIEditor::eNext;
600 break;
601 case Command::DeleteCharBackward:
602 deleteDir = nsIEditor::ePrevious;
603 break;
604 case Command::DeleteWordBackward:
605 deleteDir = nsIEditor::ePreviousWord;
606 break;
607 case Command::DeleteWordForward:
608 deleteDir = nsIEditor::eNextWord;
609 break;
610 case Command::DeleteToBeginningOfLine:
611 deleteDir = nsIEditor::eToBeginningOfLine;
612 break;
613 case Command::DeleteToEndOfLine:
614 deleteDir = nsIEditor::eToEndOfLine;
615 break;
616 default:
617 MOZ_CRASH("Unrecognized nsDeleteCommand");
619 nsresult rv = aTextEditor.DeleteSelectionAsAction(
620 deleteDir, nsIEditor::eStrip, aPrincipal);
621 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
622 "EditorBase::DeleteSelectionAsAction() failed");
623 return rv;
626 nsresult DeleteCommand::GetCommandStateParams(
627 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
628 nsIEditingSession* aEditingSession) const {
629 return aParams.SetBool(STATE_ENABLED,
630 IsCommandEnabled(aCommand, aTextEditor));
633 /******************************************************************************
634 * mozilla::SelectAllCommand
635 ******************************************************************************/
637 StaticRefPtr<SelectAllCommand> SelectAllCommand::sInstance;
639 bool SelectAllCommand::IsCommandEnabled(Command aCommand,
640 TextEditor* aTextEditor) const {
641 // You can always select all, unless the selection is editable,
642 // and the editable region is empty!
643 if (!aTextEditor) {
644 return true;
647 // You can select all if there is an editor which is non-empty
648 return !aTextEditor->IsEmpty();
651 nsresult SelectAllCommand::DoCommand(Command aCommand, TextEditor& aTextEditor,
652 nsIPrincipal* aPrincipal) const {
653 // Shouldn't cause "beforeinput" event so that we don't need to specify
654 // aPrincipal.
655 nsresult rv = aTextEditor.SelectAll();
656 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::SelectAll() failed");
657 return rv;
660 nsresult SelectAllCommand::GetCommandStateParams(
661 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
662 nsIEditingSession* aEditingSession) const {
663 return aParams.SetBool(STATE_ENABLED,
664 IsCommandEnabled(aCommand, aTextEditor));
667 /******************************************************************************
668 * mozilla::SelectionMoveCommands
669 ******************************************************************************/
671 StaticRefPtr<SelectionMoveCommands> SelectionMoveCommands::sInstance;
673 bool SelectionMoveCommands::IsCommandEnabled(Command aCommand,
674 TextEditor* aTextEditor) const {
675 if (!aTextEditor) {
676 return false;
678 return aTextEditor->IsSelectionEditable();
681 static const struct ScrollCommand {
682 Command mReverseScroll;
683 Command mForwardScroll;
684 nsresult (NS_STDCALL nsISelectionController::*scroll)(bool);
685 } scrollCommands[] = {{Command::ScrollTop, Command::ScrollBottom,
686 &nsISelectionController::CompleteScroll},
687 {Command::ScrollPageUp, Command::ScrollPageDown,
688 &nsISelectionController::ScrollPage},
689 {Command::ScrollLineUp, Command::ScrollLineDown,
690 &nsISelectionController::ScrollLine}};
692 static const struct MoveCommand {
693 Command mReverseMove;
694 Command mForwardMove;
695 Command mReverseSelect;
696 Command mForwardSelect;
697 nsresult (NS_STDCALL nsISelectionController::*move)(bool, bool);
698 } moveCommands[] = {
699 {Command::CharPrevious, Command::CharNext, Command::SelectCharPrevious,
700 Command::SelectCharNext, &nsISelectionController::CharacterMove},
701 {Command::LinePrevious, Command::LineNext, Command::SelectLinePrevious,
702 Command::SelectLineNext, &nsISelectionController::LineMove},
703 {Command::WordPrevious, Command::WordNext, Command::SelectWordPrevious,
704 Command::SelectWordNext, &nsISelectionController::WordMove},
705 {Command::BeginLine, Command::EndLine, Command::SelectBeginLine,
706 Command::SelectEndLine, &nsISelectionController::IntraLineMove},
707 {Command::MovePageUp, Command::MovePageDown, Command::SelectPageUp,
708 Command::SelectPageDown, &nsISelectionController::PageMove},
709 {Command::MoveTop, Command::MoveBottom, Command::SelectTop,
710 Command::SelectBottom, &nsISelectionController::CompleteMove}};
712 static const struct PhysicalCommand {
713 Command mMove;
714 Command mSelect;
715 int16_t direction;
716 int16_t amount;
717 } physicalCommands[] = {
718 {Command::MoveLeft, Command::SelectLeft, nsISelectionController::MOVE_LEFT,
720 {Command::MoveRight, Command::SelectRight,
721 nsISelectionController::MOVE_RIGHT, 0},
722 {Command::MoveUp, Command::SelectUp, nsISelectionController::MOVE_UP, 0},
723 {Command::MoveDown, Command::SelectDown, nsISelectionController::MOVE_DOWN,
725 {Command::MoveLeft2, Command::SelectLeft2,
726 nsISelectionController::MOVE_LEFT, 1},
727 {Command::MoveRight2, Command::SelectRight2,
728 nsISelectionController::MOVE_RIGHT, 1},
729 {Command::MoveUp2, Command::SelectUp2, nsISelectionController::MOVE_UP, 1},
730 {Command::MoveDown2, Command::SelectDown2,
731 nsISelectionController::MOVE_DOWN, 1}};
733 nsresult SelectionMoveCommands::DoCommand(Command aCommand,
734 TextEditor& aTextEditor,
735 nsIPrincipal* aPrincipal) const {
736 RefPtr<dom::Document> document = aTextEditor.GetDocument();
737 if (document) {
738 // Most of the commands below (possibly all of them) need layout to
739 // be up to date.
740 document->FlushPendingNotifications(FlushType::Layout);
743 nsCOMPtr<nsISelectionController> selectionController =
744 aTextEditor.GetSelectionController();
745 if (NS_WARN_IF(!selectionController)) {
746 return NS_ERROR_FAILURE;
749 // scroll commands
750 for (size_t i = 0; i < ArrayLength(scrollCommands); i++) {
751 const ScrollCommand& cmd = scrollCommands[i];
752 if (aCommand == cmd.mReverseScroll) {
753 return (selectionController->*(cmd.scroll))(false);
755 if (aCommand == cmd.mForwardScroll) {
756 return (selectionController->*(cmd.scroll))(true);
760 // caret movement/selection commands
761 for (size_t i = 0; i < ArrayLength(moveCommands); i++) {
762 const MoveCommand& cmd = moveCommands[i];
763 if (aCommand == cmd.mReverseMove) {
764 return (selectionController->*(cmd.move))(false, false);
766 if (aCommand == cmd.mForwardMove) {
767 return (selectionController->*(cmd.move))(true, false);
769 if (aCommand == cmd.mReverseSelect) {
770 return (selectionController->*(cmd.move))(false, true);
772 if (aCommand == cmd.mForwardSelect) {
773 return (selectionController->*(cmd.move))(true, true);
777 // physical-direction movement/selection
778 for (size_t i = 0; i < ArrayLength(physicalCommands); i++) {
779 const PhysicalCommand& cmd = physicalCommands[i];
780 if (aCommand == cmd.mMove) {
781 nsresult rv =
782 selectionController->PhysicalMove(cmd.direction, cmd.amount, false);
783 NS_WARNING_ASSERTION(
784 NS_SUCCEEDED(rv),
785 "nsISelectionController::PhysicalMove() failed to move caret");
786 return rv;
788 if (aCommand == cmd.mSelect) {
789 nsresult rv =
790 selectionController->PhysicalMove(cmd.direction, cmd.amount, true);
791 NS_WARNING_ASSERTION(
792 NS_SUCCEEDED(rv),
793 "nsISelectionController::PhysicalMove() failed to select");
794 return rv;
798 return NS_ERROR_FAILURE;
801 nsresult SelectionMoveCommands::GetCommandStateParams(
802 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
803 nsIEditingSession* aEditingSession) const {
804 return aParams.SetBool(STATE_ENABLED,
805 IsCommandEnabled(aCommand, aTextEditor));
808 /******************************************************************************
809 * mozilla::InsertPlaintextCommand
810 ******************************************************************************/
812 StaticRefPtr<InsertPlaintextCommand> InsertPlaintextCommand::sInstance;
814 bool InsertPlaintextCommand::IsCommandEnabled(Command aCommand,
815 TextEditor* aTextEditor) const {
816 if (!aTextEditor) {
817 return false;
819 return aTextEditor->IsSelectionEditable();
822 nsresult InsertPlaintextCommand::DoCommand(Command aCommand,
823 TextEditor& aTextEditor,
824 nsIPrincipal* aPrincipal) const {
825 // XXX InsertTextAsAction() is not same as OnInputText(). However, other
826 // commands to insert line break or paragraph separator use OnInput*().
827 // According to the semantics of those methods, using *AsAction() is
828 // better, however, this may not cause two or more placeholder
829 // transactions to the top transaction since its name may not be
830 // nsGkAtoms::TypingTxnName.
831 DebugOnly<nsresult> rvIgnored =
832 aTextEditor.InsertTextAsAction(u""_ns, aPrincipal);
833 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
834 "EditorBase::InsertTextAsAction() failed, but ignored");
835 return NS_OK;
838 nsresult InsertPlaintextCommand::DoCommandParam(
839 Command aCommand, const nsAString& aStringParam, TextEditor& aTextEditor,
840 nsIPrincipal* aPrincipal) const {
841 if (NS_WARN_IF(aStringParam.IsVoid())) {
842 return NS_ERROR_INVALID_ARG;
845 // XXX InsertTextAsAction() is not same as OnInputText(). However, other
846 // commands to insert line break or paragraph separator use OnInput*().
847 // According to the semantics of those methods, using *AsAction() is
848 // better, however, this may not cause two or more placeholder
849 // transactions to the top transaction since its name may not be
850 // nsGkAtoms::TypingTxnName.
851 DebugOnly<nsresult> rvIgnored =
852 aTextEditor.InsertTextAsAction(aStringParam, aPrincipal);
853 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
854 "EditorBase::InsertTextAsAction() failed, but ignored");
855 return NS_OK;
858 nsresult InsertPlaintextCommand::GetCommandStateParams(
859 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
860 nsIEditingSession* aEditingSession) const {
861 return aParams.SetBool(STATE_ENABLED,
862 IsCommandEnabled(aCommand, aTextEditor));
865 /******************************************************************************
866 * mozilla::InsertParagraphCommand
867 ******************************************************************************/
869 StaticRefPtr<InsertParagraphCommand> InsertParagraphCommand::sInstance;
871 bool InsertParagraphCommand::IsCommandEnabled(Command aCommand,
872 TextEditor* aTextEditor) const {
873 if (!aTextEditor || !aTextEditor->AsHTMLEditor()) {
874 return false;
876 return aTextEditor->IsSelectionEditable();
879 nsresult InsertParagraphCommand::DoCommand(Command aCommand,
880 TextEditor& aTextEditor,
881 nsIPrincipal* aPrincipal) const {
882 HTMLEditor* htmlEditor = aTextEditor.AsHTMLEditor();
883 if (!htmlEditor) {
884 return NS_OK; // Do nothing for now.
886 nsresult rv =
887 MOZ_KnownLive(htmlEditor)->InsertParagraphSeparatorAsAction(aPrincipal);
888 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
889 "HTMLEditor::InsertParagraphSeparatorAsAction() failed");
890 return rv;
893 nsresult InsertParagraphCommand::GetCommandStateParams(
894 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
895 nsIEditingSession* aEditingSession) const {
896 return aParams.SetBool(STATE_ENABLED,
897 IsCommandEnabled(aCommand, aTextEditor));
900 /******************************************************************************
901 * mozilla::InsertLineBreakCommand
902 ******************************************************************************/
904 StaticRefPtr<InsertLineBreakCommand> InsertLineBreakCommand::sInstance;
906 bool InsertLineBreakCommand::IsCommandEnabled(Command aCommand,
907 TextEditor* aTextEditor) const {
908 if (!aTextEditor || !aTextEditor->AsHTMLEditor()) {
909 return false;
911 return aTextEditor->IsSelectionEditable();
914 nsresult InsertLineBreakCommand::DoCommand(Command aCommand,
915 TextEditor& aTextEditor,
916 nsIPrincipal* aPrincipal) const {
917 HTMLEditor* htmlEditor = aTextEditor.AsHTMLEditor();
918 if (!htmlEditor) {
919 return NS_ERROR_FAILURE;
921 nsresult rv = MOZ_KnownLive(htmlEditor)->InsertLineBreakAsAction(aPrincipal);
922 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
923 "HTMLEditor::InsertLineBreakAsAction() failed");
924 return rv;
927 nsresult InsertLineBreakCommand::GetCommandStateParams(
928 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
929 nsIEditingSession* aEditingSession) const {
930 return aParams.SetBool(STATE_ENABLED,
931 IsCommandEnabled(aCommand, aTextEditor));
934 /******************************************************************************
935 * mozilla::PasteQuotationCommand
936 ******************************************************************************/
938 StaticRefPtr<PasteQuotationCommand> PasteQuotationCommand::sInstance;
940 bool PasteQuotationCommand::IsCommandEnabled(Command aCommand,
941 TextEditor* aTextEditor) const {
942 if (!aTextEditor) {
943 return false;
945 return !aTextEditor->IsSingleLineEditor() &&
946 aTextEditor->CanPaste(nsIClipboard::kGlobalClipboard);
949 nsresult PasteQuotationCommand::DoCommand(Command aCommand,
950 TextEditor& aTextEditor,
951 nsIPrincipal* aPrincipal) const {
952 nsresult rv = aTextEditor.PasteAsQuotationAsAction(
953 nsIClipboard::kGlobalClipboard, true, aPrincipal);
954 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
955 "TextEditor::PasteAsQuotationAsAction() failed");
956 return rv;
959 nsresult PasteQuotationCommand::GetCommandStateParams(
960 Command aCommand, nsCommandParams& aParams, TextEditor* aTextEditor,
961 nsIEditingSession* aEditingSession) const {
962 if (!aTextEditor) {
963 return NS_OK;
965 aParams.SetBool(STATE_ENABLED,
966 aTextEditor->CanPaste(nsIClipboard::kGlobalClipboard));
967 return NS_OK;
970 } // namespace mozilla