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/. */
5
6 #include "EditorCommands.h"
7
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/EditorBase.h"
11 #include "mozilla/FlushType.h"
12 #include "mozilla/HTMLEditor.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/MozPromise.h" // for mozilla::detail::Any
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"
25
26 class nsISupports;
27
28 #define STATE_ENABLED "state_enabled"
29 #define STATE_ATTRIBUTE "state_attribute"
30 #define STATE_DATA "state_data"
31
32 namespace mozilla {
33
34 using detail::Any;
35
36 /******************************************************************************
37 * mozilla::EditorCommand
38 ******************************************************************************/
39
NS_IMPL_ISUPPORTS(EditorCommand,nsIControllerCommand)40 NS_IMPL_ISUPPORTS(EditorCommand, nsIControllerCommand)
41
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;
47 }
48
49 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
50 EditorBase* editorBase = editor ? editor->AsEditorBase() : nullptr;
51 *aIsEnabled = IsCommandEnabled(GetInternalCommand(aCommandName),
52 MOZ_KnownLive(editorBase));
53 return NS_OK;
54 }
55
DoCommand(const char * aCommandName,nsISupports * aCommandRefCon)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;
60 }
61 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
62 if (NS_WARN_IF(!editor)) {
63 return NS_ERROR_INVALID_ARG;
64 }
65 nsresult rv = DoCommand(GetInternalCommand(aCommandName),
66 MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
67 NS_WARNING_ASSERTION(
68 NS_SUCCEEDED(rv),
69 "Failed to do command from nsIControllerCommand::DoCommand()");
70 return rv;
71 }
72
DoCommandParams(const char * aCommandName,nsICommandParams * aParams,nsISupports * aCommandRefCon)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;
78 }
79 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
80 if (NS_WARN_IF(!editor)) {
81 return NS_ERROR_INVALID_ARG;
82 }
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->AsEditorBase()), nullptr);
89 NS_WARNING_ASSERTION(
90 NS_SUCCEEDED(rv),
91 "Failed to do command from nsIControllerCommand::DoCommandParams()");
92 return rv;
93 }
94
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();
103 }
104 }
105 nsresult rv = DoCommandParam(
106 command, boolParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
107 NS_WARNING_ASSERTION(
108 NS_SUCCEEDED(rv),
109 "Failed to do command from nsIControllerCommand::DoCommandParams()");
110 return rv;
111 }
112 MOZ_ASSERT_UNREACHABLE("Unexpected state for bool");
113 return NS_ERROR_NOT_IMPLEMENTED;
114 }
115
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->AsEditorBase()), nullptr);
124 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
125 "Failed to do command from "
126 "nsIControllerCommand::DoCommandParams()");
127 return rv;
128 }
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->AsEditorBase()), nullptr);
137 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
138 "Failed to do command from "
139 "nsIControllerCommand::DoCommandParams()");
140 return rv;
141 }
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->AsEditorBase()), nullptr);
149 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
150 "Failed to do command from "
151 "nsIControllerCommand::DoCommandParams()");
152 return rv;
153 }
154 MOZ_ASSERT_UNREACHABLE("Unexpected state for CString/String");
155 return NS_ERROR_NOT_IMPLEMENTED;
156 }
157
158 if (Any(paramType & EditorCommandParamType::CString)) {
159 if (!params) {
160 nsresult rv =
161 DoCommandParam(command, VoidCString(),
162 MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
163 NS_WARNING_ASSERTION(
164 NS_SUCCEEDED(rv),
165 "Failed to do command from nsIControllerCommand::DoCommandParams()");
166 return rv;
167 }
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;
173 }
174 rv = DoCommandParam(command, cStringParam,
175 MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
176 NS_WARNING_ASSERTION(
177 NS_SUCCEEDED(rv),
178 "Failed to do command from nsIControllerCommand::DoCommandParams()");
179 return rv;
180 }
181 MOZ_ASSERT_UNREACHABLE("Unexpected state for CString");
182 return NS_ERROR_NOT_IMPLEMENTED;
183 }
184
185 if (Any(paramType & EditorCommandParamType::String)) {
186 if (!params) {
187 nsresult rv =
188 DoCommandParam(command, VoidString(),
189 MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
190 NS_WARNING_ASSERTION(
191 NS_SUCCEEDED(rv),
192 "Failed to do command from nsIControllerCommand::DoCommandParams()");
193 return rv;
194 }
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;
200 }
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;
205 }
206 } else {
207 MOZ_ASSERT_UNREACHABLE("Unexpected state for String");
208 return NS_ERROR_NOT_IMPLEMENTED;
209 }
210 nsresult rv = DoCommandParam(
211 command, stringParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
212 NS_WARNING_ASSERTION(
213 NS_SUCCEEDED(rv),
214 "Failed to do command from nsIControllerCommand::DoCommandParams()");
215 return rv;
216 }
217
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);
223 }
224 nsresult rv = DoCommandParam(
225 command, transferable, MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
226 NS_WARNING_ASSERTION(
227 NS_SUCCEEDED(rv),
228 "Failed to do command from nsIControllerCommand::DoCommandParams()");
229 return rv;
230 }
231
232 MOZ_ASSERT_UNREACHABLE("Unexpected param type");
233 return NS_ERROR_NOT_IMPLEMENTED;
234 }
235
GetCommandStateParams(const char * aCommandName,nsICommandParams * aParams,nsISupports * aCommandRefCon)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;
241 }
242 nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
243 if (editor) {
244 return GetCommandStateParams(GetInternalCommand(aCommandName),
245 MOZ_KnownLive(*aParams->AsCommandParams()),
246 MOZ_KnownLive(editor->AsEditorBase()),
247 nullptr);
248 }
249 nsCOMPtr<nsIEditingSession> editingSession =
250 do_QueryInterface(aCommandRefCon);
251 if (editingSession) {
252 return GetCommandStateParams(GetInternalCommand(aCommandName),
253 MOZ_KnownLive(*aParams->AsCommandParams()),
254 nullptr, editingSession);
255 }
256 return GetCommandStateParams(GetInternalCommand(aCommandName),
257 MOZ_KnownLive(*aParams->AsCommandParams()),
258 nullptr, nullptr);
259 }
260
261 /******************************************************************************
262 * mozilla::UndoCommand
263 ******************************************************************************/
264
265 StaticRefPtr<UndoCommand> UndoCommand::sInstance;
266
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const267 bool UndoCommand::IsCommandEnabled(Command aCommand,
268 EditorBase* aEditorBase) const {
269 if (!aEditorBase) {
270 return false;
271 }
272 return aEditorBase->IsSelectionEditable() && aEditorBase->CanUndo();
273 }
274
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const275 nsresult UndoCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
276 nsIPrincipal* aPrincipal) const {
277 nsresult rv = aEditorBase.UndoAsAction(1, aPrincipal);
278 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::UndoAsAction() failed");
279 return rv;
280 }
281
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const282 nsresult UndoCommand::GetCommandStateParams(
283 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
284 nsIEditingSession* aEditingSession) const {
285 return aParams.SetBool(STATE_ENABLED,
286 IsCommandEnabled(aCommand, aEditorBase));
287 }
288
289 /******************************************************************************
290 * mozilla::RedoCommand
291 ******************************************************************************/
292
293 StaticRefPtr<RedoCommand> RedoCommand::sInstance;
294
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const295 bool RedoCommand::IsCommandEnabled(Command aCommand,
296 EditorBase* aEditorBase) const {
297 if (!aEditorBase) {
298 return false;
299 }
300 return aEditorBase->IsSelectionEditable() && aEditorBase->CanRedo();
301 }
302
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const303 nsresult RedoCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
304 nsIPrincipal* aPrincipal) const {
305 nsresult rv = aEditorBase.RedoAsAction(1, aPrincipal);
306 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::RedoAsAction() failed");
307 return rv;
308 }
309
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const310 nsresult RedoCommand::GetCommandStateParams(
311 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
312 nsIEditingSession* aEditingSession) const {
313 return aParams.SetBool(STATE_ENABLED,
314 IsCommandEnabled(aCommand, aEditorBase));
315 }
316
317 /******************************************************************************
318 * mozilla::CutCommand
319 ******************************************************************************/
320
321 StaticRefPtr<CutCommand> CutCommand::sInstance;
322
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const323 bool CutCommand::IsCommandEnabled(Command aCommand,
324 EditorBase* aEditorBase) const {
325 if (!aEditorBase) {
326 return false;
327 }
328 return aEditorBase->IsSelectionEditable() &&
329 aEditorBase->IsCutCommandEnabled();
330 }
331
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const332 nsresult CutCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
333 nsIPrincipal* aPrincipal) const {
334 nsresult rv = aEditorBase.CutAsAction(aPrincipal);
335 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed");
336 return rv;
337 }
338
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const339 nsresult CutCommand::GetCommandStateParams(
340 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
341 nsIEditingSession* aEditingSession) const {
342 return aParams.SetBool(STATE_ENABLED,
343 IsCommandEnabled(aCommand, aEditorBase));
344 }
345
346 /******************************************************************************
347 * mozilla::CutOrDeleteCommand
348 ******************************************************************************/
349
350 StaticRefPtr<CutOrDeleteCommand> CutOrDeleteCommand::sInstance;
351
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const352 bool CutOrDeleteCommand::IsCommandEnabled(Command aCommand,
353 EditorBase* aEditorBase) const {
354 if (!aEditorBase) {
355 return false;
356 }
357 return aEditorBase->IsSelectionEditable();
358 }
359
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const360 nsresult CutOrDeleteCommand::DoCommand(Command aCommand,
361 EditorBase& aEditorBase,
362 nsIPrincipal* aPrincipal) const {
363 dom::Selection* selection = aEditorBase.GetSelection();
364 if (selection && selection->IsCollapsed()) {
365 nsresult rv = aEditorBase.DeleteSelectionAsAction(
366 nsIEditor::eNext, nsIEditor::eStrip, aPrincipal);
367 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
368 "EditorBase::DeleteSelectionAsAction() failed");
369 return rv;
370 }
371 nsresult rv = aEditorBase.CutAsAction(aPrincipal);
372 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed");
373 return rv;
374 }
375
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const376 nsresult CutOrDeleteCommand::GetCommandStateParams(
377 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
378 nsIEditingSession* aEditingSession) const {
379 return aParams.SetBool(STATE_ENABLED,
380 IsCommandEnabled(aCommand, aEditorBase));
381 }
382
383 /******************************************************************************
384 * mozilla::CopyCommand
385 ******************************************************************************/
386
387 StaticRefPtr<CopyCommand> CopyCommand::sInstance;
388
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const389 bool CopyCommand::IsCommandEnabled(Command aCommand,
390 EditorBase* aEditorBase) const {
391 if (!aEditorBase) {
392 return false;
393 }
394 return aEditorBase->IsCopyCommandEnabled();
395 }
396
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const397 nsresult CopyCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
398 nsIPrincipal* aPrincipal) const {
399 // Shouldn't cause "beforeinput" event so that we don't need to specify
400 // the given principal.
401 return aEditorBase.Copy();
402 }
403
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const404 nsresult CopyCommand::GetCommandStateParams(
405 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
406 nsIEditingSession* aEditingSession) const {
407 return aParams.SetBool(STATE_ENABLED,
408 IsCommandEnabled(aCommand, aEditorBase));
409 }
410
411 /******************************************************************************
412 * mozilla::CopyOrDeleteCommand
413 ******************************************************************************/
414
415 StaticRefPtr<CopyOrDeleteCommand> CopyOrDeleteCommand::sInstance;
416
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const417 bool CopyOrDeleteCommand::IsCommandEnabled(Command aCommand,
418 EditorBase* aEditorBase) const {
419 if (!aEditorBase) {
420 return false;
421 }
422 return aEditorBase->IsSelectionEditable();
423 }
424
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const425 nsresult CopyOrDeleteCommand::DoCommand(Command aCommand,
426 EditorBase& aEditorBase,
427 nsIPrincipal* aPrincipal) const {
428 dom::Selection* selection = aEditorBase.GetSelection();
429 if (selection && selection->IsCollapsed()) {
430 nsresult rv = aEditorBase.DeleteSelectionAsAction(
431 nsIEditor::eNextWord, nsIEditor::eStrip, aPrincipal);
432 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
433 "EditorBase::DeleteSelectionAsAction() failed");
434 return rv;
435 }
436 // Shouldn't cause "beforeinput" event so that we don't need to specify
437 // the given principal.
438 nsresult rv = aEditorBase.Copy();
439 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::Copy() failed");
440 return rv;
441 }
442
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const443 nsresult CopyOrDeleteCommand::GetCommandStateParams(
444 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
445 nsIEditingSession* aEditingSession) const {
446 return aParams.SetBool(STATE_ENABLED,
447 IsCommandEnabled(aCommand, aEditorBase));
448 }
449
450 /******************************************************************************
451 * mozilla::PasteCommand
452 ******************************************************************************/
453
454 StaticRefPtr<PasteCommand> PasteCommand::sInstance;
455
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const456 bool PasteCommand::IsCommandEnabled(Command aCommand,
457 EditorBase* aEditorBase) const {
458 if (!aEditorBase) {
459 return false;
460 }
461 return aEditorBase->IsSelectionEditable() &&
462 aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard);
463 }
464
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const465 nsresult PasteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
466 nsIPrincipal* aPrincipal) const {
467 nsresult rv = aEditorBase.PasteAsAction(nsIClipboard::kGlobalClipboard, true,
468 aPrincipal);
469 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::PasteAsAction() failed");
470 return rv;
471 }
472
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const473 nsresult PasteCommand::GetCommandStateParams(
474 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
475 nsIEditingSession* aEditingSession) const {
476 return aParams.SetBool(STATE_ENABLED,
477 IsCommandEnabled(aCommand, aEditorBase));
478 }
479
480 /******************************************************************************
481 * mozilla::PasteTransferableCommand
482 ******************************************************************************/
483
484 StaticRefPtr<PasteTransferableCommand> PasteTransferableCommand::sInstance;
485
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const486 bool PasteTransferableCommand::IsCommandEnabled(Command aCommand,
487 EditorBase* aEditorBase) const {
488 if (!aEditorBase) {
489 return false;
490 }
491 return aEditorBase->IsSelectionEditable() &&
492 aEditorBase->CanPasteTransferable(nullptr);
493 }
494
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const495 nsresult PasteTransferableCommand::DoCommand(Command aCommand,
496 EditorBase& aEditorBase,
497 nsIPrincipal* aPrincipal) const {
498 return NS_ERROR_FAILURE;
499 }
500
DoCommandParam(Command aCommand,nsITransferable * aTransferableParam,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const501 nsresult PasteTransferableCommand::DoCommandParam(
502 Command aCommand, nsITransferable* aTransferableParam,
503 EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const {
504 if (NS_WARN_IF(!aTransferableParam)) {
505 return NS_ERROR_INVALID_ARG;
506 }
507 nsresult rv =
508 aEditorBase.PasteTransferableAsAction(aTransferableParam, aPrincipal);
509 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
510 "EditorBase::PasteTransferableAsAction() failed");
511 return rv;
512 }
513
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const514 nsresult PasteTransferableCommand::GetCommandStateParams(
515 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
516 nsIEditingSession* aEditingSession) const {
517 if (NS_WARN_IF(!aEditorBase)) {
518 return NS_ERROR_INVALID_ARG;
519 }
520
521 nsCOMPtr<nsISupports> supports = aParams.GetISupports("transferable");
522 if (NS_WARN_IF(!supports)) {
523 return NS_ERROR_FAILURE;
524 }
525
526 nsCOMPtr<nsITransferable> trans;
527 trans = do_QueryInterface(supports);
528 if (NS_WARN_IF(!trans)) {
529 return NS_ERROR_FAILURE;
530 }
531
532 return aParams.SetBool(STATE_ENABLED,
533 aEditorBase->CanPasteTransferable(trans));
534 }
535
536 /******************************************************************************
537 * mozilla::SwitchTextDirectionCommand
538 ******************************************************************************/
539
540 StaticRefPtr<SwitchTextDirectionCommand> SwitchTextDirectionCommand::sInstance;
541
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const542 bool SwitchTextDirectionCommand::IsCommandEnabled(
543 Command aCommand, EditorBase* aEditorBase) const {
544 if (!aEditorBase) {
545 return false;
546 }
547 return aEditorBase->IsSelectionEditable();
548 }
549
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const550 nsresult SwitchTextDirectionCommand::DoCommand(Command aCommand,
551 EditorBase& aEditorBase,
552 nsIPrincipal* aPrincipal) const {
553 nsresult rv = aEditorBase.ToggleTextDirectionAsAction(aPrincipal);
554 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
555 "EditorBase::ToggleTextDirectionAsAction() failed");
556 return rv;
557 }
558
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const559 nsresult SwitchTextDirectionCommand::GetCommandStateParams(
560 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
561 nsIEditingSession* aEditingSession) const {
562 return aParams.SetBool(STATE_ENABLED,
563 IsCommandEnabled(aCommand, aEditorBase));
564 }
565
566 /******************************************************************************
567 * mozilla::DeleteCommand
568 ******************************************************************************/
569
570 StaticRefPtr<DeleteCommand> DeleteCommand::sInstance;
571
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const572 bool DeleteCommand::IsCommandEnabled(Command aCommand,
573 EditorBase* aEditorBase) const {
574 if (!aEditorBase) {
575 return false;
576 }
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 = aEditorBase->IsSelectionEditable();
581
582 if (aCommand == Command::Delete && isEnabled) {
583 return aEditorBase->CanDeleteSelection();
584 }
585 return isEnabled;
586 }
587
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const588 nsresult DeleteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
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");
618 }
619 nsresult rv = aEditorBase.DeleteSelectionAsAction(
620 deleteDir, nsIEditor::eStrip, aPrincipal);
621 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
622 "EditorBase::DeleteSelectionAsAction() failed");
623 return rv;
624 }
625
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const626 nsresult DeleteCommand::GetCommandStateParams(
627 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
628 nsIEditingSession* aEditingSession) const {
629 return aParams.SetBool(STATE_ENABLED,
630 IsCommandEnabled(aCommand, aEditorBase));
631 }
632
633 /******************************************************************************
634 * mozilla::SelectAllCommand
635 ******************************************************************************/
636
637 StaticRefPtr<SelectAllCommand> SelectAllCommand::sInstance;
638
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const639 bool SelectAllCommand::IsCommandEnabled(Command aCommand,
640 EditorBase* aEditorBase) const {
641 // You can always select all, unless the selection is editable,
642 // and the editable region is empty!
643 if (!aEditorBase) {
644 return true;
645 }
646
647 // You can select all if there is an editor which is non-empty
648 return !aEditorBase->IsEmpty();
649 }
650
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const651 nsresult SelectAllCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
652 nsIPrincipal* aPrincipal) const {
653 // Shouldn't cause "beforeinput" event so that we don't need to specify
654 // aPrincipal.
655 nsresult rv = aEditorBase.SelectAll();
656 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::SelectAll() failed");
657 return rv;
658 }
659
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const660 nsresult SelectAllCommand::GetCommandStateParams(
661 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
662 nsIEditingSession* aEditingSession) const {
663 return aParams.SetBool(STATE_ENABLED,
664 IsCommandEnabled(aCommand, aEditorBase));
665 }
666
667 /******************************************************************************
668 * mozilla::SelectionMoveCommands
669 ******************************************************************************/
670
671 StaticRefPtr<SelectionMoveCommands> SelectionMoveCommands::sInstance;
672
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const673 bool SelectionMoveCommands::IsCommandEnabled(Command aCommand,
674 EditorBase* aEditorBase) const {
675 if (!aEditorBase) {
676 return false;
677 }
678 return aEditorBase->IsSelectionEditable();
679 }
680
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}};
691
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}};
711
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,
719 0},
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,
724 0},
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}};
732
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const733 nsresult SelectionMoveCommands::DoCommand(Command aCommand,
734 EditorBase& aEditorBase,
735 nsIPrincipal* aPrincipal) const {
736 RefPtr<dom::Document> document = aEditorBase.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);
741 }
742
743 nsCOMPtr<nsISelectionController> selectionController =
744 aEditorBase.GetSelectionController();
745 if (NS_WARN_IF(!selectionController)) {
746 return NS_ERROR_FAILURE;
747 }
748
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);
754 }
755 if (aCommand == cmd.mForwardScroll) {
756 return (selectionController->*(cmd.scroll))(true);
757 }
758 }
759
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);
765 }
766 if (aCommand == cmd.mForwardMove) {
767 return (selectionController->*(cmd.move))(true, false);
768 }
769 if (aCommand == cmd.mReverseSelect) {
770 return (selectionController->*(cmd.move))(false, true);
771 }
772 if (aCommand == cmd.mForwardSelect) {
773 return (selectionController->*(cmd.move))(true, true);
774 }
775 }
776
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;
787 }
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;
795 }
796 }
797
798 return NS_ERROR_FAILURE;
799 }
800
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const801 nsresult SelectionMoveCommands::GetCommandStateParams(
802 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
803 nsIEditingSession* aEditingSession) const {
804 return aParams.SetBool(STATE_ENABLED,
805 IsCommandEnabled(aCommand, aEditorBase));
806 }
807
808 /******************************************************************************
809 * mozilla::InsertPlaintextCommand
810 ******************************************************************************/
811
812 StaticRefPtr<InsertPlaintextCommand> InsertPlaintextCommand::sInstance;
813
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const814 bool InsertPlaintextCommand::IsCommandEnabled(Command aCommand,
815 EditorBase* aEditorBase) const {
816 if (!aEditorBase) {
817 return false;
818 }
819 return aEditorBase->IsSelectionEditable();
820 }
821
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const822 nsresult InsertPlaintextCommand::DoCommand(Command aCommand,
823 EditorBase& aEditorBase,
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 aEditorBase.InsertTextAsAction(u""_ns, aPrincipal);
833 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
834 "EditorBase::InsertTextAsAction() failed, but ignored");
835 return NS_OK;
836 }
837
DoCommandParam(Command aCommand,const nsAString & aStringParam,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const838 nsresult InsertPlaintextCommand::DoCommandParam(
839 Command aCommand, const nsAString& aStringParam, EditorBase& aEditorBase,
840 nsIPrincipal* aPrincipal) const {
841 if (NS_WARN_IF(aStringParam.IsVoid())) {
842 return NS_ERROR_INVALID_ARG;
843 }
844
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 aEditorBase.InsertTextAsAction(aStringParam, aPrincipal);
853 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
854 "EditorBase::InsertTextAsAction() failed, but ignored");
855 return NS_OK;
856 }
857
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const858 nsresult InsertPlaintextCommand::GetCommandStateParams(
859 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
860 nsIEditingSession* aEditingSession) const {
861 return aParams.SetBool(STATE_ENABLED,
862 IsCommandEnabled(aCommand, aEditorBase));
863 }
864
865 /******************************************************************************
866 * mozilla::InsertParagraphCommand
867 ******************************************************************************/
868
869 StaticRefPtr<InsertParagraphCommand> InsertParagraphCommand::sInstance;
870
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const871 bool InsertParagraphCommand::IsCommandEnabled(Command aCommand,
872 EditorBase* aEditorBase) const {
873 if (!aEditorBase || aEditorBase->IsSingleLineEditor()) {
874 return false;
875 }
876 return aEditorBase->IsSelectionEditable();
877 }
878
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const879 nsresult InsertParagraphCommand::DoCommand(Command aCommand,
880 EditorBase& aEditorBase,
881 nsIPrincipal* aPrincipal) const {
882 if (aEditorBase.IsSingleLineEditor()) {
883 return NS_ERROR_FAILURE;
884 }
885 if (aEditorBase.IsHTMLEditor()) {
886 nsresult rv = MOZ_KnownLive(aEditorBase.AsHTMLEditor())
887 ->InsertParagraphSeparatorAsAction(aPrincipal);
888 NS_WARNING_ASSERTION(
889 NS_SUCCEEDED(rv),
890 "HTMLEditor::InsertParagraphSeparatorAsAction() failed");
891 return rv;
892 }
893 nsresult rv = aEditorBase.InsertLineBreakAsAction(aPrincipal);
894 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
895 "EditorBase::InsertLineBreakAsAction() failed");
896 return rv;
897 }
898
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const899 nsresult InsertParagraphCommand::GetCommandStateParams(
900 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
901 nsIEditingSession* aEditingSession) const {
902 return aParams.SetBool(STATE_ENABLED,
903 IsCommandEnabled(aCommand, aEditorBase));
904 }
905
906 /******************************************************************************
907 * mozilla::InsertLineBreakCommand
908 ******************************************************************************/
909
910 StaticRefPtr<InsertLineBreakCommand> InsertLineBreakCommand::sInstance;
911
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const912 bool InsertLineBreakCommand::IsCommandEnabled(Command aCommand,
913 EditorBase* aEditorBase) const {
914 if (!aEditorBase || aEditorBase->IsSingleLineEditor()) {
915 return false;
916 }
917 return aEditorBase->IsSelectionEditable();
918 }
919
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const920 nsresult InsertLineBreakCommand::DoCommand(Command aCommand,
921 EditorBase& aEditorBase,
922 nsIPrincipal* aPrincipal) const {
923 if (aEditorBase.IsSingleLineEditor()) {
924 return NS_ERROR_FAILURE;
925 }
926 nsresult rv = aEditorBase.InsertLineBreakAsAction(aPrincipal);
927 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
928 "EditorBase::InsertLineBreakAsAction() failed");
929 return rv;
930 }
931
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const932 nsresult InsertLineBreakCommand::GetCommandStateParams(
933 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
934 nsIEditingSession* aEditingSession) const {
935 return aParams.SetBool(STATE_ENABLED,
936 IsCommandEnabled(aCommand, aEditorBase));
937 }
938
939 /******************************************************************************
940 * mozilla::PasteQuotationCommand
941 ******************************************************************************/
942
943 StaticRefPtr<PasteQuotationCommand> PasteQuotationCommand::sInstance;
944
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const945 bool PasteQuotationCommand::IsCommandEnabled(Command aCommand,
946 EditorBase* aEditorBase) const {
947 if (!aEditorBase) {
948 return false;
949 }
950 return !aEditorBase->IsSingleLineEditor() &&
951 aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard);
952 }
953
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const954 nsresult PasteQuotationCommand::DoCommand(Command aCommand,
955 EditorBase& aEditorBase,
956 nsIPrincipal* aPrincipal) const {
957 nsresult rv = aEditorBase.PasteAsQuotationAsAction(
958 nsIClipboard::kGlobalClipboard, true, aPrincipal);
959 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
960 "EditorBase::PasteAsQuotationAsAction() failed");
961 return rv;
962 }
963
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const964 nsresult PasteQuotationCommand::GetCommandStateParams(
965 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
966 nsIEditingSession* aEditingSession) const {
967 if (!aEditorBase) {
968 return NS_OK;
969 }
970 aParams.SetBool(STATE_ENABLED,
971 aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard));
972 return NS_OK;
973 }
974
975 } // namespace mozilla
976