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"
24 #include "nsAString.h"
28 #define STATE_ENABLED "state_enabled"
29 #define STATE_ATTRIBUTE "state_attribute"
30 #define STATE_DATA "state_data"
36 /******************************************************************************
37 * mozilla::EditorCommand
38 ******************************************************************************/
40 NS_IMPL_ISUPPORTS(EditorCommand
, nsIControllerCommand
)
42 NS_IMETHODIMP
EditorCommand::IsCommandEnabled(const char* aCommandName
,
43 nsISupports
* aCommandRefCon
,
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
));
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);
69 "Failed to do command from nsIControllerCommand::DoCommand()");
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);
91 "Failed to do command from nsIControllerCommand::DoCommandParams()");
95 if (Any(paramType
& EditorCommandParamType::Bool
)) {
96 if (Any(paramType
& EditorCommandParamType::StateAttribute
)) {
97 Maybe
<bool> boolParam
= Nothing();
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(
109 "Failed to do command from nsIControllerCommand::DoCommandParams()");
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
)) {
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()");
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
);
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()");
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()");
154 MOZ_ASSERT_UNREACHABLE("Unexpected state for CString/String");
155 return NS_ERROR_NOT_IMPLEMENTED
;
158 if (Any(paramType
& EditorCommandParamType::CString
)) {
161 DoCommandParam(command
, VoidCString(),
162 MOZ_KnownLive(*editor
->AsTextEditor()), nullptr);
163 NS_WARNING_ASSERTION(
165 "Failed to do command from nsIControllerCommand::DoCommandParams()");
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
))) {
174 rv
= DoCommandParam(command
, cStringParam
,
175 MOZ_KnownLive(*editor
->AsTextEditor()), nullptr);
176 NS_WARNING_ASSERTION(
178 "Failed to do command from nsIControllerCommand::DoCommandParams()");
181 MOZ_ASSERT_UNREACHABLE("Unexpected state for CString");
182 return NS_ERROR_NOT_IMPLEMENTED
;
185 if (Any(paramType
& EditorCommandParamType::String
)) {
188 DoCommandParam(command
, VoidString(),
189 MOZ_KnownLive(*editor
->AsTextEditor()), nullptr);
190 NS_WARNING_ASSERTION(
192 "Failed to do command from nsIControllerCommand::DoCommandParams()");
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
))) {
201 } else if (Any(paramType
& EditorCommandParamType::StateData
)) {
202 nsresult rv
= params
->GetString(STATE_DATA
, stringParam
);
203 if (NS_WARN_IF(NS_FAILED(rv
))) {
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(
214 "Failed to do command from nsIControllerCommand::DoCommandParams()");
218 if (Any(paramType
& EditorCommandParamType::Transferable
)) {
219 nsCOMPtr
<nsITransferable
> transferable
;
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(
228 "Failed to do command from nsIControllerCommand::DoCommandParams()");
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
);
244 return GetCommandStateParams(GetInternalCommand(aCommandName
),
245 MOZ_KnownLive(*aParams
->AsCommandParams()),
246 MOZ_KnownLive(editor
->AsTextEditor()),
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()),
261 /******************************************************************************
262 * mozilla::UndoCommand
263 ******************************************************************************/
265 StaticRefPtr
<UndoCommand
> UndoCommand::sInstance
;
267 bool UndoCommand::IsCommandEnabled(Command aCommand
,
268 TextEditor
* aTextEditor
) const {
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");
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 {
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");
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 {
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");
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 {
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");
371 nsresult rv
= aTextEditor
.CutAsAction(aPrincipal
);
372 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "TextEditor::CutAsAction() failed");
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 {
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 {
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");
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");
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 {
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,
469 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "TextEditor::PasteAsAction() failed");
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 {
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
;
508 aTextEditor
.PasteTransferableAsAction(aTransferableParam
, aPrincipal
);
509 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
510 "TextEditor::PasteTransferableAsAction() failed");
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 {
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");
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 {
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
580 bool isEnabled
= aTextEditor
->IsSelectionEditable();
582 if (aCommand
== Command::Delete
&& isEnabled
) {
583 return aTextEditor
->CanDeleteSelection();
588 nsresult
DeleteCommand::DoCommand(Command aCommand
, TextEditor
& aTextEditor
,
589 nsIPrincipal
* aPrincipal
) const {
590 nsIEditor::EDirection deleteDir
= nsIEditor::eNone
;
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
;
598 case Command::DeleteCharForward
:
599 deleteDir
= nsIEditor::eNext
;
601 case Command::DeleteCharBackward
:
602 deleteDir
= nsIEditor::ePrevious
;
604 case Command::DeleteWordBackward
:
605 deleteDir
= nsIEditor::ePreviousWord
;
607 case Command::DeleteWordForward
:
608 deleteDir
= nsIEditor::eNextWord
;
610 case Command::DeleteToBeginningOfLine
:
611 deleteDir
= nsIEditor::eToBeginningOfLine
;
613 case Command::DeleteToEndOfLine
:
614 deleteDir
= nsIEditor::eToEndOfLine
;
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");
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!
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
655 nsresult rv
= aTextEditor
.SelectAll();
656 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "EditorBase::SelectAll() failed");
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 {
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);
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
{
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();
738 // Most of the commands below (possibly all of them) need layout to
740 document
->FlushPendingNotifications(FlushType::Layout
);
743 nsCOMPtr
<nsISelectionController
> selectionController
=
744 aTextEditor
.GetSelectionController();
745 if (NS_WARN_IF(!selectionController
)) {
746 return NS_ERROR_FAILURE
;
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
) {
782 selectionController
->PhysicalMove(cmd
.direction
, cmd
.amount
, false);
783 NS_WARNING_ASSERTION(
785 "nsISelectionController::PhysicalMove() failed to move caret");
788 if (aCommand
== cmd
.mSelect
) {
790 selectionController
->PhysicalMove(cmd
.direction
, cmd
.amount
, true);
791 NS_WARNING_ASSERTION(
793 "nsISelectionController::PhysicalMove() failed to select");
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 {
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");
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");
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()) {
876 return aTextEditor
->IsSelectionEditable();
879 nsresult
InsertParagraphCommand::DoCommand(Command aCommand
,
880 TextEditor
& aTextEditor
,
881 nsIPrincipal
* aPrincipal
) const {
882 HTMLEditor
* htmlEditor
= aTextEditor
.AsHTMLEditor();
884 return NS_OK
; // Do nothing for now.
887 MOZ_KnownLive(htmlEditor
)->InsertParagraphSeparatorAsAction(aPrincipal
);
888 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
889 "HTMLEditor::InsertParagraphSeparatorAsAction() failed");
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()) {
911 return aTextEditor
->IsSelectionEditable();
914 nsresult
InsertLineBreakCommand::DoCommand(Command aCommand
,
915 TextEditor
& aTextEditor
,
916 nsIPrincipal
* aPrincipal
) const {
917 HTMLEditor
* htmlEditor
= aTextEditor
.AsHTMLEditor();
919 return NS_ERROR_FAILURE
;
921 nsresult rv
= MOZ_KnownLive(htmlEditor
)->InsertLineBreakAsAction(aPrincipal
);
922 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
923 "HTMLEditor::InsertLineBreakAsAction() failed");
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 {
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");
959 nsresult
PasteQuotationCommand::GetCommandStateParams(
960 Command aCommand
, nsCommandParams
& aParams
, TextEditor
* aTextEditor
,
961 nsIEditingSession
* aEditingSession
) const {
965 aParams
.SetBool(STATE_ENABLED
,
966 aTextEditor
->CanPaste(nsIClipboard::kGlobalClipboard
));
970 } // namespace mozilla