Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / editor / libeditor / HTMLEditorCommands.cpp
blobbdf3116e23718680af26b0d002139d9cfade51fe
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 "mozilla/EditorCommands.h"
8 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
9 #include "mozilla/EditorBase.h" // for EditorBase
10 #include "mozilla/ErrorResult.h"
11 #include "mozilla/HTMLEditor.h" // for HTMLEditor
12 #include "mozilla/dom/Element.h"
13 #include "nsAString.h"
14 #include "nsAtom.h" // for nsAtom, nsStaticAtom, etc
15 #include "nsCommandParams.h" // for nsCommandParams, etc
16 #include "nsComponentManagerUtils.h" // for do_CreateInstance
17 #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::font, etc
18 #include "nsIClipboard.h" // for nsIClipboard, etc
19 #include "nsIEditingSession.h"
20 #include "nsIPrincipal.h" // for nsIPrincipal
21 #include "nsLiteralString.h" // for NS_LITERAL_STRING
22 #include "nsReadableUtils.h" // for EmptyString
23 #include "nsString.h" // for nsAutoString, nsString, etc
24 #include "nsStringFwd.h" // for nsString
26 class nsISupports;
28 namespace mozilla {
29 using dom::Element;
31 // prototype
32 static nsresult GetListState(HTMLEditor* aHTMLEditor, bool* aMixed,
33 nsAString& aLocalName);
35 // defines
36 #define STATE_ENABLED "state_enabled"
37 #define STATE_ALL "state_all"
38 #define STATE_ANY "state_any"
39 #define STATE_MIXED "state_mixed"
40 #define STATE_BEGIN "state_begin"
41 #define STATE_END "state_end"
42 #define STATE_ATTRIBUTE "state_attribute"
43 #define STATE_DATA "state_data"
45 /*****************************************************************************
46 * mozilla::StateUpdatingCommandBase
47 *****************************************************************************/
49 bool StateUpdatingCommandBase::IsCommandEnabled(Command aCommand,
50 EditorBase* aEditorBase) const {
51 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
52 if (!htmlEditor) {
53 return false;
55 if (!htmlEditor->IsModifiable() || !htmlEditor->IsSelectionEditable()) {
56 return false;
58 if (aCommand == Command::FormatAbsolutePosition) {
59 return htmlEditor->IsAbsolutePositionEditorEnabled();
61 return true;
64 nsresult StateUpdatingCommandBase::DoCommand(Command aCommand,
65 EditorBase& aEditorBase,
66 nsIPrincipal* aPrincipal) const {
67 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
68 if (NS_WARN_IF(!htmlEditor)) {
69 return NS_ERROR_FAILURE;
71 nsStaticAtom* tagName = GetTagName(aCommand);
72 if (NS_WARN_IF(!tagName)) {
73 return NS_ERROR_UNEXPECTED;
75 nsresult rv = ToggleState(MOZ_KnownLive(*tagName), MOZ_KnownLive(*htmlEditor),
76 aPrincipal);
77 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
78 "StateUpdatingCommandBase::ToggleState() failed");
79 return rv;
82 nsresult StateUpdatingCommandBase::GetCommandStateParams(
83 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
84 nsIEditingSession* aEditingSession) const {
85 if (!aEditorBase) {
86 return NS_OK;
88 HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor();
89 if (NS_WARN_IF(!htmlEditor)) {
90 return NS_ERROR_FAILURE;
92 nsStaticAtom* tagName = GetTagName(aCommand);
93 if (NS_WARN_IF(!tagName)) {
94 return NS_ERROR_UNEXPECTED;
96 // MOZ_KnownLive(htmlEditor) because the lifetime of aEditorBase is guaranteed
97 // by the callers.
98 // MOZ_KnownLive(tagName) because nsStaticAtom instances are alive until
99 // shutting down.
100 nsresult rv = GetCurrentState(MOZ_KnownLive(*tagName),
101 MOZ_KnownLive(*htmlEditor), aParams);
102 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
103 "StateUpdatingCommandBase::GetCurrentState() failed");
104 return rv;
107 /*****************************************************************************
108 * mozilla::PasteNoFormattingCommand
109 *****************************************************************************/
111 StaticRefPtr<PasteNoFormattingCommand> PasteNoFormattingCommand::sInstance;
113 bool PasteNoFormattingCommand::IsCommandEnabled(Command aCommand,
114 EditorBase* aEditorBase) const {
115 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
116 if (!htmlEditor) {
117 return false;
119 return htmlEditor->CanPaste(nsIClipboard::kGlobalClipboard);
122 nsresult PasteNoFormattingCommand::DoCommand(Command aCommand,
123 EditorBase& aEditorBase,
124 nsIPrincipal* aPrincipal) const {
125 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
126 if (NS_WARN_IF(!htmlEditor)) {
127 return NS_ERROR_FAILURE;
129 // Known live because we hold a ref above in "editor"
130 nsresult rv =
131 MOZ_KnownLive(htmlEditor)
132 ->PasteNoFormattingAsAction(nsIClipboard::kGlobalClipboard,
133 EditorBase::DispatchPasteEvent::Yes,
134 nullptr, aPrincipal);
135 NS_WARNING_ASSERTION(
136 NS_SUCCEEDED(rv),
137 "HTMLEditor::PasteNoFormattingAsAction(DispatchPasteEvent::Yes) failed");
138 return rv;
141 nsresult PasteNoFormattingCommand::GetCommandStateParams(
142 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
143 nsIEditingSession* aEditingSession) const {
144 return aParams.SetBool(STATE_ENABLED,
145 IsCommandEnabled(aCommand, aEditorBase));
148 /*****************************************************************************
149 * mozilla::StyleUpdatingCommand
150 *****************************************************************************/
152 StaticRefPtr<StyleUpdatingCommand> StyleUpdatingCommand::sInstance;
154 nsresult StyleUpdatingCommand::GetCurrentState(nsStaticAtom& aTagName,
155 HTMLEditor& aHTMLEditor,
156 nsCommandParams& aParams) const {
157 bool firstOfSelectionHasProp = false;
158 bool anyOfSelectionHasProp = false;
159 bool allOfSelectionHasProp = false;
161 nsresult rv = aHTMLEditor.GetInlineProperty(
162 aTagName, nullptr, u""_ns, &firstOfSelectionHasProp,
163 &anyOfSelectionHasProp, &allOfSelectionHasProp);
164 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
165 "HTMLEditor::GetInlineProperty() failed");
167 aParams.SetBool(STATE_ENABLED, NS_SUCCEEDED(rv));
168 aParams.SetBool(STATE_ALL, allOfSelectionHasProp);
169 aParams.SetBool(STATE_ANY, anyOfSelectionHasProp);
170 aParams.SetBool(STATE_MIXED, anyOfSelectionHasProp && !allOfSelectionHasProp);
171 aParams.SetBool(STATE_BEGIN, firstOfSelectionHasProp);
172 aParams.SetBool(STATE_END, allOfSelectionHasProp); // not completely accurate
173 return NS_OK;
176 nsresult StyleUpdatingCommand::ToggleState(nsStaticAtom& aTagName,
177 HTMLEditor& aHTMLEditor,
178 nsIPrincipal* aPrincipal) const {
179 RefPtr<nsCommandParams> params = new nsCommandParams();
181 // tags "href" and "name" are special cases in the core editor
182 // they are used to remove named anchor/link and shouldn't be used for
183 // insertion
184 bool doTagRemoval;
185 if (&aTagName == nsGkAtoms::href || &aTagName == nsGkAtoms::name) {
186 doTagRemoval = true;
187 } else {
188 // check current selection; set doTagRemoval if formatting should be removed
189 nsresult rv = GetCurrentState(aTagName, aHTMLEditor, *params);
190 if (NS_FAILED(rv)) {
191 NS_WARNING("StyleUpdatingCommand::GetCurrentState() failed");
192 return rv;
194 ErrorResult error;
195 doTagRemoval = params->GetBool(STATE_ALL, error);
196 if (NS_WARN_IF(error.Failed())) {
197 return error.StealNSResult();
201 if (doTagRemoval) {
202 nsresult rv =
203 aHTMLEditor.RemoveInlinePropertyAsAction(aTagName, nullptr, aPrincipal);
204 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
205 "HTMLEditor::RemoveInlinePropertyAsAction() failed");
206 return rv;
209 nsresult rv = aHTMLEditor.SetInlinePropertyAsAction(aTagName, nullptr, u""_ns,
210 aPrincipal);
211 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
212 "HTMLEditor::SetInlinePropertyAsAction() failed");
213 return rv;
216 /*****************************************************************************
217 * mozilla::ListCommand
218 *****************************************************************************/
220 StaticRefPtr<ListCommand> ListCommand::sInstance;
222 nsresult ListCommand::GetCurrentState(nsStaticAtom& aTagName,
223 HTMLEditor& aHTMLEditor,
224 nsCommandParams& aParams) const {
225 bool bMixed;
226 nsAutoString localName;
227 nsresult rv = GetListState(&aHTMLEditor, &bMixed, localName);
228 if (NS_FAILED(rv)) {
229 NS_WARNING("GetListState() failed");
230 return rv;
233 bool inList = aTagName.Equals(localName);
234 aParams.SetBool(STATE_ALL, !bMixed && inList);
235 aParams.SetBool(STATE_MIXED, bMixed);
236 aParams.SetBool(STATE_ENABLED, true);
237 return NS_OK;
240 nsresult ListCommand::ToggleState(nsStaticAtom& aTagName,
241 HTMLEditor& aHTMLEditor,
242 nsIPrincipal* aPrincipal) const {
243 RefPtr<nsCommandParams> params = new nsCommandParams();
244 nsresult rv = GetCurrentState(aTagName, aHTMLEditor, *params);
245 if (NS_FAILED(rv)) {
246 NS_WARNING("ListCommand::GetCurrentState() failed");
247 return rv;
250 ErrorResult error;
251 bool inList = params->GetBool(STATE_ALL, error);
252 if (NS_WARN_IF(error.Failed())) {
253 return error.StealNSResult();
256 nsDependentAtomString listType(&aTagName);
257 if (inList) {
258 nsresult rv = aHTMLEditor.RemoveListAsAction(listType, aPrincipal);
259 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
260 "HTMLEditor::RemoveListAsAction() failed");
261 return rv;
264 rv = aHTMLEditor.MakeOrChangeListAsAction(
265 aTagName, u""_ns, HTMLEditor::SelectAllOfCurrentList::No, aPrincipal);
266 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
267 "HTMLEditor::MakeOrChangeListAsAction() failed");
268 return rv;
271 /*****************************************************************************
272 * mozilla::ListItemCommand
273 *****************************************************************************/
275 StaticRefPtr<ListItemCommand> ListItemCommand::sInstance;
277 nsresult ListItemCommand::GetCurrentState(nsStaticAtom& aTagName,
278 HTMLEditor& aHTMLEditor,
279 nsCommandParams& aParams) const {
280 ErrorResult error;
281 ListItemElementSelectionState state(aHTMLEditor, error);
282 if (error.Failed()) {
283 NS_WARNING("ListItemElementSelectionState failed");
284 return error.StealNSResult();
287 if (state.IsNotOneTypeDefinitionListItemElementSelected()) {
288 aParams.SetBool(STATE_ALL, false);
289 aParams.SetBool(STATE_MIXED, true);
290 return NS_OK;
293 nsStaticAtom* selectedListItemTagName = nullptr;
294 if (state.IsLIElementSelected()) {
295 selectedListItemTagName = nsGkAtoms::li;
296 } else if (state.IsDTElementSelected()) {
297 selectedListItemTagName = nsGkAtoms::dt;
298 } else if (state.IsDDElementSelected()) {
299 selectedListItemTagName = nsGkAtoms::dd;
301 aParams.SetBool(STATE_ALL, &aTagName == selectedListItemTagName);
302 aParams.SetBool(STATE_MIXED, false);
303 return NS_OK;
306 nsresult ListItemCommand::ToggleState(nsStaticAtom& aTagName,
307 HTMLEditor& aHTMLEditor,
308 nsIPrincipal* aPrincipal) const {
309 // Need to use aTagName????
310 RefPtr<nsCommandParams> params = new nsCommandParams();
311 GetCurrentState(aTagName, aHTMLEditor, *params);
312 ErrorResult error;
313 bool inList = params->GetBool(STATE_ALL, error);
314 if (NS_WARN_IF(error.Failed())) {
315 return error.StealNSResult();
318 if (inList) {
319 // To remove a list, first get what kind of list we're in
320 bool bMixed;
321 nsAutoString localName;
322 nsresult rv = GetListState(&aHTMLEditor, &bMixed, localName);
323 if (NS_FAILED(rv)) {
324 NS_WARNING("GetListState() failed");
325 return rv;
327 if (localName.IsEmpty() || bMixed) {
328 return NS_OK;
330 rv = aHTMLEditor.RemoveListAsAction(localName, aPrincipal);
331 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
332 "HTMLEditor::RemoveListAsAction() failed");
333 return rv;
336 // Set to the requested paragraph type
337 // XXX Note: This actually doesn't work for "LI",
338 // but we currently don't use this for non DL lists anyway.
339 // Problem: won't this replace any current block paragraph style?
340 nsresult rv = aHTMLEditor.SetParagraphStateAsAction(
341 nsDependentAtomString(&aTagName), aPrincipal);
342 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
343 "HTMLEditor::SetParagraphFormatAsAction() failed");
344 return rv;
347 /*****************************************************************************
348 * mozilla::RemoveListCommand
349 *****************************************************************************/
351 StaticRefPtr<RemoveListCommand> RemoveListCommand::sInstance;
353 bool RemoveListCommand::IsCommandEnabled(Command aCommand,
354 EditorBase* aEditorBase) const {
355 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
356 if (!htmlEditor) {
357 return false;
359 if (!htmlEditor->IsModifiable() || !htmlEditor->IsSelectionEditable()) {
360 return false;
363 // It is enabled if we are in any list type
364 bool bMixed;
365 nsAutoString localName;
366 nsresult rv = GetListState(MOZ_KnownLive(htmlEditor), &bMixed, localName);
367 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetListState() failed");
368 return NS_SUCCEEDED(rv) && (bMixed || !localName.IsEmpty());
371 nsresult RemoveListCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
372 nsIPrincipal* aPrincipal) const {
373 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
374 if (NS_WARN_IF(!htmlEditor)) {
375 return NS_OK;
377 // This removes any list type
378 nsresult rv =
379 MOZ_KnownLive(htmlEditor)->RemoveListAsAction(u""_ns, aPrincipal);
380 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
381 "HTMLEditor::RemoveListAsAction() failed");
382 return rv;
385 nsresult RemoveListCommand::GetCommandStateParams(
386 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
387 nsIEditingSession* aEditingSession) const {
388 return aParams.SetBool(STATE_ENABLED,
389 IsCommandEnabled(aCommand, aEditorBase));
392 /*****************************************************************************
393 * mozilla::IndentCommand
394 *****************************************************************************/
396 StaticRefPtr<IndentCommand> IndentCommand::sInstance;
398 bool IndentCommand::IsCommandEnabled(Command aCommand,
399 EditorBase* aEditorBase) const {
400 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
401 if (!htmlEditor) {
402 return false;
404 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
407 nsresult IndentCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
408 nsIPrincipal* aPrincipal) const {
409 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
410 if (NS_WARN_IF(!htmlEditor)) {
411 return NS_OK;
413 nsresult rv = MOZ_KnownLive(htmlEditor)->IndentAsAction(aPrincipal);
414 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::IndentAsAction() failed");
415 return rv;
418 nsresult IndentCommand::GetCommandStateParams(
419 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
420 nsIEditingSession* aEditingSession) const {
421 return aParams.SetBool(STATE_ENABLED,
422 IsCommandEnabled(aCommand, aEditorBase));
425 /*****************************************************************************
426 * mozilla::OutdentCommand
427 *****************************************************************************/
429 StaticRefPtr<OutdentCommand> OutdentCommand::sInstance;
431 bool OutdentCommand::IsCommandEnabled(Command aCommand,
432 EditorBase* aEditorBase) const {
433 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
434 if (!htmlEditor) {
435 return false;
437 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
440 nsresult OutdentCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
441 nsIPrincipal* aPrincipal) const {
442 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
443 if (NS_WARN_IF(!htmlEditor)) {
444 return NS_OK;
446 nsresult rv = MOZ_KnownLive(htmlEditor)->OutdentAsAction(aPrincipal);
447 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
448 "HTMLEditor::OutdentAsAction() failed");
449 return rv;
452 nsresult OutdentCommand::GetCommandStateParams(
453 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
454 nsIEditingSession* aEditingSession) const {
455 return aParams.SetBool(STATE_ENABLED,
456 IsCommandEnabled(aCommand, aEditorBase));
459 /*****************************************************************************
460 * mozilla::MultiStateCommandBase
461 *****************************************************************************/
463 bool MultiStateCommandBase::IsCommandEnabled(Command aCommand,
464 EditorBase* aEditorBase) const {
465 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
466 if (!htmlEditor) {
467 return false;
469 // should be disabled sometimes, like if the current selection is an image
470 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
473 nsresult MultiStateCommandBase::DoCommand(Command aCommand,
474 EditorBase& aEditorBase,
475 nsIPrincipal* aPrincipal) const {
476 NS_WARNING(
477 "who is calling MultiStateCommandBase::DoCommand (no implementation)?");
478 return NS_OK;
481 nsresult MultiStateCommandBase::DoCommandParam(Command aCommand,
482 const nsAString& aStringParam,
483 EditorBase& aEditorBase,
484 nsIPrincipal* aPrincipal) const {
485 NS_WARNING_ASSERTION(aCommand != Command::FormatJustify,
486 "Command::FormatJustify should be used only for "
487 "IsCommandEnabled() and GetCommandStateParams()");
488 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
489 if (NS_WARN_IF(!htmlEditor)) {
490 return NS_ERROR_FAILURE;
492 nsresult rv = SetState(MOZ_KnownLive(htmlEditor), aStringParam, aPrincipal);
493 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
494 "MultiStateCommandBase::SetState() failed");
495 return rv;
498 nsresult MultiStateCommandBase::GetCommandStateParams(
499 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
500 nsIEditingSession* aEditingSession) const {
501 if (!aEditorBase) {
502 return NS_OK;
504 HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor();
505 if (NS_WARN_IF(!htmlEditor)) {
506 return NS_ERROR_FAILURE;
508 nsresult rv = GetCurrentState(MOZ_KnownLive(htmlEditor), aParams);
509 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
510 "MultiStateCommandBase::GetCurrentState() failed");
511 return rv;
514 /*****************************************************************************
515 * mozilla::FormatBlockStateCommand
516 *****************************************************************************/
518 StaticRefPtr<FormatBlockStateCommand> FormatBlockStateCommand::sInstance;
520 nsresult FormatBlockStateCommand::GetCurrentState(
521 HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
522 if (NS_WARN_IF(!aHTMLEditor)) {
523 return NS_ERROR_INVALID_ARG;
526 ErrorResult error;
527 ParagraphStateAtSelection state(
528 *aHTMLEditor,
529 ParagraphStateAtSelection::FormatBlockMode::HTMLFormatBlockCommand,
530 error);
531 if (error.Failed()) {
532 NS_WARNING("ParagraphStateAtSelection failed");
533 return error.StealNSResult();
535 aParams.SetBool(STATE_MIXED, state.IsMixed());
536 if (NS_WARN_IF(!state.GetFirstParagraphStateAtSelection())) {
537 aParams.SetCString(STATE_ATTRIBUTE, ""_ns);
538 } else {
539 nsCString paragraphState; // Don't use `nsAutoCString` for avoiding copy.
540 state.GetFirstParagraphStateAtSelection()->ToUTF8String(paragraphState);
541 aParams.SetCString(STATE_ATTRIBUTE, paragraphState);
543 return NS_OK;
546 nsresult FormatBlockStateCommand::SetState(HTMLEditor* aHTMLEditor,
547 const nsAString& aNewState,
548 nsIPrincipal* aPrincipal) const {
549 if (NS_WARN_IF(!aHTMLEditor) || NS_WARN_IF(aNewState.IsEmpty())) {
550 return NS_ERROR_INVALID_ARG;
552 nsresult rv = aHTMLEditor->FormatBlockAsAction(aNewState, aPrincipal);
553 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
554 "HTMLEditor::FormatBlockAsAction() failed");
555 return rv;
558 /*****************************************************************************
559 * mozilla::ParagraphStateCommand
560 *****************************************************************************/
562 StaticRefPtr<ParagraphStateCommand> ParagraphStateCommand::sInstance;
564 nsresult ParagraphStateCommand::GetCurrentState(
565 HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
566 if (NS_WARN_IF(!aHTMLEditor)) {
567 return NS_ERROR_INVALID_ARG;
570 ErrorResult error;
571 ParagraphStateAtSelection state(
572 *aHTMLEditor,
573 ParagraphStateAtSelection::FormatBlockMode::XULParagraphStateCommand,
574 error);
575 if (error.Failed()) {
576 NS_WARNING("ParagraphStateAtSelection failed");
577 return error.StealNSResult();
579 aParams.SetBool(STATE_MIXED, state.IsMixed());
580 if (NS_WARN_IF(!state.GetFirstParagraphStateAtSelection())) {
581 // XXX This is odd behavior, we should fix this later.
582 aParams.SetCString(STATE_ATTRIBUTE, "x"_ns);
583 } else {
584 nsCString paragraphState; // Don't use `nsAutoCString` for avoiding copy.
585 state.GetFirstParagraphStateAtSelection()->ToUTF8String(paragraphState);
586 aParams.SetCString(STATE_ATTRIBUTE, paragraphState);
588 return NS_OK;
591 nsresult ParagraphStateCommand::SetState(HTMLEditor* aHTMLEditor,
592 const nsAString& aNewState,
593 nsIPrincipal* aPrincipal) const {
594 if (NS_WARN_IF(!aHTMLEditor)) {
595 return NS_ERROR_INVALID_ARG;
597 nsresult rv = aHTMLEditor->SetParagraphStateAsAction(aNewState, aPrincipal);
598 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
599 "HTMLEditor::SetParagraphStateAsAction() failed");
600 return rv;
603 /*****************************************************************************
604 * mozilla::FontFaceStateCommand
605 *****************************************************************************/
607 StaticRefPtr<FontFaceStateCommand> FontFaceStateCommand::sInstance;
609 nsresult FontFaceStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
610 nsCommandParams& aParams) const {
611 if (NS_WARN_IF(!aHTMLEditor)) {
612 return NS_ERROR_INVALID_ARG;
615 nsAutoString outStateString;
616 bool outMixed;
617 nsresult rv = aHTMLEditor->GetFontFaceState(&outMixed, outStateString);
618 if (NS_FAILED(rv)) {
619 NS_WARNING("HTMLEditor::GetFontFaceState() failed");
620 return rv;
622 aParams.SetBool(STATE_MIXED, outMixed);
623 aParams.SetCString(STATE_ATTRIBUTE, NS_ConvertUTF16toUTF8(outStateString));
624 return NS_OK;
627 nsresult FontFaceStateCommand::SetState(HTMLEditor* aHTMLEditor,
628 const nsAString& aNewState,
629 nsIPrincipal* aPrincipal) const {
630 if (NS_WARN_IF(!aHTMLEditor)) {
631 return NS_ERROR_INVALID_ARG;
634 if (aNewState.IsEmpty() || aNewState.EqualsLiteral("normal")) {
635 nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
636 *nsGkAtoms::font, nsGkAtoms::face, aPrincipal);
637 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
638 "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
639 "font, nsGkAtoms::face) failed");
640 return rv;
643 nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
644 *nsGkAtoms::font, nsGkAtoms::face, aNewState, aPrincipal);
645 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
646 "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::font, "
647 "nsGkAtoms::face) failed");
648 return rv;
651 /*****************************************************************************
652 * mozilla::FontSizeStateCommand
653 *****************************************************************************/
655 StaticRefPtr<FontSizeStateCommand> FontSizeStateCommand::sInstance;
657 nsresult FontSizeStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
658 nsCommandParams& aParams) const {
659 if (NS_WARN_IF(!aHTMLEditor)) {
660 return NS_ERROR_INVALID_ARG;
663 nsAutoString outStateString;
664 bool firstHas, anyHas, allHas;
665 nsresult rv = aHTMLEditor->GetInlinePropertyWithAttrValue(
666 *nsGkAtoms::font, nsGkAtoms::size, u""_ns, &firstHas, &anyHas, &allHas,
667 outStateString);
668 if (NS_FAILED(rv)) {
669 NS_WARNING(
670 "HTMLEditor::GetInlinePropertyWithAttrValue(nsGkAtoms::font, "
671 "nsGkAtoms::size) failed");
672 return rv;
675 nsAutoCString tOutStateString;
676 LossyCopyUTF16toASCII(outStateString, tOutStateString);
677 aParams.SetBool(STATE_MIXED, anyHas && !allHas);
678 aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
679 aParams.SetBool(STATE_ENABLED, true);
681 return NS_OK;
684 // acceptable values for "aNewState" are:
685 // -2
686 // -1
687 // 0
688 // +1
689 // +2
690 // +3
691 // medium
692 // normal
693 nsresult FontSizeStateCommand::SetState(HTMLEditor* aHTMLEditor,
694 const nsAString& aNewState,
695 nsIPrincipal* aPrincipal) const {
696 if (NS_WARN_IF(!aHTMLEditor)) {
697 return NS_ERROR_INVALID_ARG;
700 if (!aNewState.IsEmpty() && !aNewState.EqualsLiteral("normal") &&
701 !aNewState.EqualsLiteral("medium")) {
702 nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
703 *nsGkAtoms::font, nsGkAtoms::size, aNewState, aPrincipal);
704 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
705 "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::"
706 "font, nsGkAtoms::size) failed");
707 return rv;
710 // remove any existing font size, big or small
711 nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
712 *nsGkAtoms::font, nsGkAtoms::size, aPrincipal);
713 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
714 "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
715 "font, nsGkAtoms::size) failed");
716 return rv;
719 /*****************************************************************************
720 * mozilla::FontColorStateCommand
721 *****************************************************************************/
723 StaticRefPtr<FontColorStateCommand> FontColorStateCommand::sInstance;
725 nsresult FontColorStateCommand::GetCurrentState(
726 HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
727 if (NS_WARN_IF(!aHTMLEditor)) {
728 return NS_ERROR_INVALID_ARG;
731 bool outMixed;
732 nsAutoString outStateString;
733 nsresult rv = aHTMLEditor->GetFontColorState(&outMixed, outStateString);
734 if (NS_FAILED(rv)) {
735 NS_WARNING("HTMLEditor::GetFontColorState() failed");
736 return rv;
739 nsAutoCString tOutStateString;
740 LossyCopyUTF16toASCII(outStateString, tOutStateString);
741 aParams.SetBool(STATE_MIXED, outMixed);
742 aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
743 return NS_OK;
746 nsresult FontColorStateCommand::SetState(HTMLEditor* aHTMLEditor,
747 const nsAString& aNewState,
748 nsIPrincipal* aPrincipal) const {
749 if (NS_WARN_IF(!aHTMLEditor)) {
750 return NS_ERROR_INVALID_ARG;
753 if (aNewState.IsEmpty() || aNewState.EqualsLiteral("normal")) {
754 nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
755 *nsGkAtoms::font, nsGkAtoms::color, aPrincipal);
756 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
757 "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
758 "font, nsGkAtoms::color) failed");
759 return rv;
762 nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
763 *nsGkAtoms::font, nsGkAtoms::color, aNewState, aPrincipal);
764 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
765 "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::font, "
766 "nsGkAtoms::color) failed");
767 return rv;
770 /*****************************************************************************
771 * mozilla::HighlightColorStateCommand
772 *****************************************************************************/
774 StaticRefPtr<HighlightColorStateCommand> HighlightColorStateCommand::sInstance;
776 nsresult HighlightColorStateCommand::GetCurrentState(
777 HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
778 if (NS_WARN_IF(!aHTMLEditor)) {
779 return NS_ERROR_INVALID_ARG;
782 bool outMixed;
783 nsAutoString outStateString;
784 nsresult rv = aHTMLEditor->GetHighlightColorState(&outMixed, outStateString);
785 if (NS_FAILED(rv)) {
786 NS_WARNING("HTMLEditor::GetHighlightColorState() failed");
787 return rv;
790 nsAutoCString tOutStateString;
791 LossyCopyUTF16toASCII(outStateString, tOutStateString);
792 aParams.SetBool(STATE_MIXED, outMixed);
793 aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
794 return NS_OK;
797 nsresult HighlightColorStateCommand::SetState(HTMLEditor* aHTMLEditor,
798 const nsAString& aNewState,
799 nsIPrincipal* aPrincipal) const {
800 if (NS_WARN_IF(!aHTMLEditor)) {
801 return NS_ERROR_INVALID_ARG;
804 if (aNewState.IsEmpty() || aNewState.EqualsLiteral("normal")) {
805 nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
806 *nsGkAtoms::font, nsGkAtoms::bgcolor, aPrincipal);
807 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
808 "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
809 "font, nsGkAtoms::bgcolor) failed");
810 return rv;
813 nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
814 *nsGkAtoms::font, nsGkAtoms::bgcolor, aNewState, aPrincipal);
815 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
816 "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::font, "
817 "nsGkAtoms::bgcolor) failed");
818 return rv;
821 /*****************************************************************************
822 * mozilla::BackgroundColorStateCommand
823 *****************************************************************************/
825 StaticRefPtr<BackgroundColorStateCommand>
826 BackgroundColorStateCommand::sInstance;
828 nsresult BackgroundColorStateCommand::GetCurrentState(
829 HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
830 if (NS_WARN_IF(!aHTMLEditor)) {
831 return NS_ERROR_INVALID_ARG;
834 bool outMixed;
835 nsAutoString outStateString;
836 nsresult rv = aHTMLEditor->GetBackgroundColorState(&outMixed, outStateString);
837 if (NS_FAILED(rv)) {
838 NS_WARNING("HTMLEditor::GetBackgroundColorState() failed");
839 return rv;
842 nsAutoCString tOutStateString;
843 LossyCopyUTF16toASCII(outStateString, tOutStateString);
844 aParams.SetBool(STATE_MIXED, outMixed);
845 aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
846 return NS_OK;
849 nsresult BackgroundColorStateCommand::SetState(HTMLEditor* aHTMLEditor,
850 const nsAString& aNewState,
851 nsIPrincipal* aPrincipal) const {
852 if (NS_WARN_IF(!aHTMLEditor)) {
853 return NS_ERROR_INVALID_ARG;
855 nsresult rv = aHTMLEditor->SetBackgroundColorAsAction(aNewState, aPrincipal);
856 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
857 "HTMLEditor::SetBackgroundColorAsAction() failed");
858 return rv;
861 /*****************************************************************************
862 * mozilla::AlignCommand
863 *****************************************************************************/
865 StaticRefPtr<AlignCommand> AlignCommand::sInstance;
867 nsresult AlignCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
868 nsCommandParams& aParams) const {
869 if (NS_WARN_IF(!aHTMLEditor)) {
870 return NS_ERROR_INVALID_ARG;
873 ErrorResult error;
874 AlignStateAtSelection state(*aHTMLEditor, error);
875 if (error.Failed()) {
876 if (!state.IsSelectionRangesFound()) {
877 // If there was no selection ranges, we shouldn't throw exception for
878 // compatibility with the other browsers, but I have no better idea
879 // than returning empty string in this case. Oddly, Blink/WebKit returns
880 // "true" or "false", but it's different from us and the value does not
881 // make sense. Additionally, WPT loves our behavior.
882 error.SuppressException();
883 aParams.SetBool(STATE_MIXED, false);
884 aParams.SetCString(STATE_ATTRIBUTE, ""_ns);
885 return NS_OK;
887 NS_WARNING("AlignStateAtSelection failed");
888 return error.StealNSResult();
890 nsCString alignment; // Don't use `nsAutoCString` to avoid copying string.
891 switch (state.AlignmentAtSelectionStart()) {
892 default:
893 case nsIHTMLEditor::eLeft:
894 alignment.AssignLiteral("left");
895 break;
896 case nsIHTMLEditor::eCenter:
897 alignment.AssignLiteral("center");
898 break;
899 case nsIHTMLEditor::eRight:
900 alignment.AssignLiteral("right");
901 break;
902 case nsIHTMLEditor::eJustify:
903 alignment.AssignLiteral("justify");
904 break;
906 aParams.SetBool(STATE_MIXED, false);
907 aParams.SetCString(STATE_ATTRIBUTE, alignment);
908 return NS_OK;
911 nsresult AlignCommand::SetState(HTMLEditor* aHTMLEditor,
912 const nsAString& aNewState,
913 nsIPrincipal* aPrincipal) const {
914 if (NS_WARN_IF(!aHTMLEditor)) {
915 return NS_ERROR_INVALID_ARG;
917 nsresult rv = aHTMLEditor->AlignAsAction(aNewState, aPrincipal);
918 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::AlignAsAction() failed");
919 return rv;
922 /*****************************************************************************
923 * mozilla::AbsolutePositioningCommand
924 *****************************************************************************/
926 StaticRefPtr<AbsolutePositioningCommand> AbsolutePositioningCommand::sInstance;
928 nsresult AbsolutePositioningCommand::GetCurrentState(
929 nsStaticAtom& aTagName, HTMLEditor& aHTMLEditor,
930 nsCommandParams& aParams) const {
931 if (!aHTMLEditor.IsAbsolutePositionEditorEnabled()) {
932 aParams.SetBool(STATE_MIXED, false);
933 aParams.SetCString(STATE_ATTRIBUTE, ""_ns);
934 return NS_OK;
937 RefPtr<Element> container =
938 aHTMLEditor.GetAbsolutelyPositionedSelectionContainer();
939 aParams.SetBool(STATE_MIXED, false);
940 aParams.SetCString(STATE_ATTRIBUTE, container ? "absolute"_ns : ""_ns);
941 return NS_OK;
944 nsresult AbsolutePositioningCommand::ToggleState(
945 nsStaticAtom& aTagName, HTMLEditor& aHTMLEditor,
946 nsIPrincipal* aPrincipal) const {
947 RefPtr<Element> container =
948 aHTMLEditor.GetAbsolutelyPositionedSelectionContainer();
949 nsresult rv = aHTMLEditor.SetSelectionToAbsoluteOrStaticAsAction(!container,
950 aPrincipal);
951 NS_WARNING_ASSERTION(
952 NS_SUCCEEDED(rv),
953 "HTMLEditor::SetSelectionToAbsoluteOrStaticAsAction() failed");
954 return rv;
957 /*****************************************************************************
958 * mozilla::DecreaseZIndexCommand
959 *****************************************************************************/
961 StaticRefPtr<DecreaseZIndexCommand> DecreaseZIndexCommand::sInstance;
963 bool DecreaseZIndexCommand::IsCommandEnabled(Command aCommand,
964 EditorBase* aEditorBase) const {
965 RefPtr<HTMLEditor> htmlEditor = HTMLEditor::GetFrom(aEditorBase);
966 if (!htmlEditor) {
967 return false;
969 if (!htmlEditor->IsAbsolutePositionEditorEnabled()) {
970 return false;
972 RefPtr<Element> positionedElement = htmlEditor->GetPositionedElement();
973 if (!positionedElement) {
974 return false;
976 return htmlEditor->GetZIndex(*positionedElement) > 0;
979 nsresult DecreaseZIndexCommand::DoCommand(Command aCommand,
980 EditorBase& aEditorBase,
981 nsIPrincipal* aPrincipal) const {
982 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
983 if (NS_WARN_IF(!htmlEditor)) {
984 return NS_ERROR_FAILURE;
986 nsresult rv = MOZ_KnownLive(htmlEditor)->AddZIndexAsAction(-1, aPrincipal);
987 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
988 "HTMLEditor::AddZIndexAsAction(-1) failed");
989 return rv;
992 nsresult DecreaseZIndexCommand::GetCommandStateParams(
993 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
994 nsIEditingSession* aEditingSession) const {
995 return aParams.SetBool(STATE_ENABLED,
996 IsCommandEnabled(aCommand, aEditorBase));
999 /*****************************************************************************
1000 * mozilla::IncreaseZIndexCommand
1001 *****************************************************************************/
1003 StaticRefPtr<IncreaseZIndexCommand> IncreaseZIndexCommand::sInstance;
1005 bool IncreaseZIndexCommand::IsCommandEnabled(Command aCommand,
1006 EditorBase* aEditorBase) const {
1007 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1008 if (!htmlEditor) {
1009 return false;
1011 if (!htmlEditor->IsAbsolutePositionEditorEnabled()) {
1012 return false;
1014 return !!htmlEditor->GetPositionedElement();
1017 nsresult IncreaseZIndexCommand::DoCommand(Command aCommand,
1018 EditorBase& aEditorBase,
1019 nsIPrincipal* aPrincipal) const {
1020 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1021 if (NS_WARN_IF(!htmlEditor)) {
1022 return NS_ERROR_FAILURE;
1024 nsresult rv = MOZ_KnownLive(htmlEditor)->AddZIndexAsAction(1, aPrincipal);
1025 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1026 "HTMLEditor::AddZIndexAsAction(1) failed");
1027 return rv;
1030 nsresult IncreaseZIndexCommand::GetCommandStateParams(
1031 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1032 nsIEditingSession* aEditingSession) const {
1033 return aParams.SetBool(STATE_ENABLED,
1034 IsCommandEnabled(aCommand, aEditorBase));
1037 /*****************************************************************************
1038 * mozilla::RemoveStylesCommand
1039 *****************************************************************************/
1041 StaticRefPtr<RemoveStylesCommand> RemoveStylesCommand::sInstance;
1043 bool RemoveStylesCommand::IsCommandEnabled(Command aCommand,
1044 EditorBase* aEditorBase) const {
1045 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1046 if (!htmlEditor) {
1047 return false;
1049 // test if we have any styles?
1050 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
1053 nsresult RemoveStylesCommand::DoCommand(Command aCommand,
1054 EditorBase& aEditorBase,
1055 nsIPrincipal* aPrincipal) const {
1056 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1057 if (NS_WARN_IF(!htmlEditor)) {
1058 return NS_OK;
1060 nsresult rv =
1061 MOZ_KnownLive(htmlEditor)->RemoveAllInlinePropertiesAsAction(aPrincipal);
1062 NS_WARNING_ASSERTION(
1063 NS_SUCCEEDED(rv),
1064 "HTMLEditor::RemoveAllInlinePropertiesAsAction() failed");
1065 return rv;
1068 nsresult RemoveStylesCommand::GetCommandStateParams(
1069 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1070 nsIEditingSession* aEditingSession) const {
1071 return aParams.SetBool(STATE_ENABLED,
1072 IsCommandEnabled(aCommand, aEditorBase));
1075 /*****************************************************************************
1076 * mozilla::IncreaseFontSizeCommand
1077 *****************************************************************************/
1079 StaticRefPtr<IncreaseFontSizeCommand> IncreaseFontSizeCommand::sInstance;
1081 bool IncreaseFontSizeCommand::IsCommandEnabled(Command aCommand,
1082 EditorBase* aEditorBase) const {
1083 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1084 if (!htmlEditor) {
1085 return false;
1087 // test if we are at max size?
1088 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
1091 nsresult IncreaseFontSizeCommand::DoCommand(Command aCommand,
1092 EditorBase& aEditorBase,
1093 nsIPrincipal* aPrincipal) const {
1094 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1095 if (NS_WARN_IF(!htmlEditor)) {
1096 return NS_OK;
1098 nsresult rv = MOZ_KnownLive(htmlEditor)->IncreaseFontSizeAsAction(aPrincipal);
1099 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1100 "HTMLEditor::IncreaseFontSizeAsAction() failed");
1101 return rv;
1104 nsresult IncreaseFontSizeCommand::GetCommandStateParams(
1105 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1106 nsIEditingSession* aEditingSession) const {
1107 return aParams.SetBool(STATE_ENABLED,
1108 IsCommandEnabled(aCommand, aEditorBase));
1111 /*****************************************************************************
1112 * mozilla::DecreaseFontSizeCommand
1113 *****************************************************************************/
1115 StaticRefPtr<DecreaseFontSizeCommand> DecreaseFontSizeCommand::sInstance;
1117 bool DecreaseFontSizeCommand::IsCommandEnabled(Command aCommand,
1118 EditorBase* aEditorBase) const {
1119 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1120 if (!htmlEditor) {
1121 return false;
1123 // test if we are at min size?
1124 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
1127 nsresult DecreaseFontSizeCommand::DoCommand(Command aCommand,
1128 EditorBase& aEditorBase,
1129 nsIPrincipal* aPrincipal) const {
1130 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1131 if (NS_WARN_IF(!htmlEditor)) {
1132 return NS_OK;
1134 nsresult rv = MOZ_KnownLive(htmlEditor)->DecreaseFontSizeAsAction(aPrincipal);
1135 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1136 "HTMLEditor::DecreaseFontSizeAsAction() failed");
1137 return rv;
1140 nsresult DecreaseFontSizeCommand::GetCommandStateParams(
1141 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1142 nsIEditingSession* aEditingSession) const {
1143 return aParams.SetBool(STATE_ENABLED,
1144 IsCommandEnabled(aCommand, aEditorBase));
1147 /*****************************************************************************
1148 * mozilla::InsertHTMLCommand
1149 *****************************************************************************/
1151 StaticRefPtr<InsertHTMLCommand> InsertHTMLCommand::sInstance;
1153 bool InsertHTMLCommand::IsCommandEnabled(Command aCommand,
1154 EditorBase* aEditorBase) const {
1155 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1156 if (!htmlEditor) {
1157 return false;
1159 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
1162 nsresult InsertHTMLCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
1163 nsIPrincipal* aPrincipal) const {
1164 // If InsertHTMLCommand is called with no parameters, it was probably called
1165 // with an empty string parameter ''. In this case, it should act the same as
1166 // the delete command
1167 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1168 if (NS_WARN_IF(!htmlEditor)) {
1169 return NS_ERROR_FAILURE;
1171 nsresult rv =
1172 MOZ_KnownLive(htmlEditor)->InsertHTMLAsAction(u""_ns, aPrincipal);
1173 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1174 "HTMLEditor::InsertHTMLAsAction() failed");
1175 return rv;
1178 nsresult InsertHTMLCommand::DoCommandParam(Command aCommand,
1179 const nsAString& aStringParam,
1180 EditorBase& aEditorBase,
1181 nsIPrincipal* aPrincipal) const {
1182 if (NS_WARN_IF(aStringParam.IsVoid())) {
1183 return NS_ERROR_INVALID_ARG;
1186 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1187 if (NS_WARN_IF(!htmlEditor)) {
1188 return NS_ERROR_FAILURE;
1190 nsresult rv =
1191 MOZ_KnownLive(htmlEditor)->InsertHTMLAsAction(aStringParam, aPrincipal);
1192 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1193 "HTMLEditor::InsertHTMLAsAction() failed");
1194 return rv;
1197 nsresult InsertHTMLCommand::GetCommandStateParams(
1198 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1199 nsIEditingSession* aEditingSession) const {
1200 return aParams.SetBool(STATE_ENABLED,
1201 IsCommandEnabled(aCommand, aEditorBase));
1204 /*****************************************************************************
1205 * mozilla::InsertTagCommand
1206 *****************************************************************************/
1208 StaticRefPtr<InsertTagCommand> InsertTagCommand::sInstance;
1210 bool InsertTagCommand::IsCommandEnabled(Command aCommand,
1211 EditorBase* aEditorBase) const {
1212 HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1213 if (!htmlEditor) {
1214 return false;
1216 return htmlEditor->IsModifiable() && htmlEditor->IsSelectionEditable();
1219 // corresponding STATE_ATTRIBUTE is: src (img) and href (a)
1220 nsresult InsertTagCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
1221 nsIPrincipal* aPrincipal) const {
1222 nsAtom* tagName = GetTagName(aCommand);
1223 if (NS_WARN_IF(tagName != nsGkAtoms::hr)) {
1224 return NS_ERROR_NOT_IMPLEMENTED;
1227 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1228 if (NS_WARN_IF(!htmlEditor)) {
1229 return NS_ERROR_FAILURE;
1232 RefPtr<Element> newElement =
1233 MOZ_KnownLive(htmlEditor)
1234 ->CreateElementWithDefaults(MOZ_KnownLive(*tagName));
1235 if (NS_WARN_IF(!newElement)) {
1236 return NS_ERROR_FAILURE;
1238 HTMLEditor::InsertElementOptions options{
1239 HTMLEditor::InsertElementOption::DeleteSelection};
1240 // We did insert <img> without splitting ancestor inline elements, but the
1241 // other browsers split them. Therefore, let's do it only when the document
1242 // is content.
1243 if (tagName == nsGkAtoms::img &&
1244 htmlEditor->GetDocument()->IsContentDocument()) {
1245 options += HTMLEditor::InsertElementOption::SplitAncestorInlineElements;
1247 nsresult rv =
1248 MOZ_KnownLive(htmlEditor)
1249 ->InsertElementAtSelectionAsAction(newElement, options, aPrincipal);
1250 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1251 "HTMLEditor::InsertElementAtSelectionAsAction() failed");
1252 return rv;
1255 nsresult InsertTagCommand::DoCommandParam(Command aCommand,
1256 const nsAString& aStringParam,
1257 EditorBase& aEditorBase,
1258 nsIPrincipal* aPrincipal) const {
1259 MOZ_ASSERT(aCommand != Command::InsertHorizontalRule);
1261 if (NS_WARN_IF(aStringParam.IsEmpty())) {
1262 return NS_ERROR_INVALID_ARG;
1264 nsAtom* tagName = GetTagName(aCommand);
1265 if (NS_WARN_IF(!tagName)) {
1266 return NS_ERROR_UNEXPECTED;
1269 HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1270 if (NS_WARN_IF(!htmlEditor)) {
1271 return NS_ERROR_FAILURE;
1274 // filter out tags we don't know how to insert
1275 nsAtom* attribute = nullptr;
1276 if (tagName == nsGkAtoms::a) {
1277 attribute = nsGkAtoms::href;
1278 } else if (tagName == nsGkAtoms::img) {
1279 attribute = nsGkAtoms::src;
1280 } else {
1281 return NS_ERROR_NOT_IMPLEMENTED;
1284 RefPtr<Element> newElement =
1285 MOZ_KnownLive(htmlEditor)
1286 ->CreateElementWithDefaults(MOZ_KnownLive(*tagName));
1287 if (!newElement) {
1288 NS_WARNING("HTMLEditor::CreateElementWithDefaults() failed");
1289 return NS_ERROR_FAILURE;
1292 ErrorResult error;
1293 newElement->SetAttr(attribute, aStringParam, error);
1294 if (error.Failed()) {
1295 NS_WARNING("Element::SetAttr() failed");
1296 return error.StealNSResult();
1299 // do actual insertion
1300 if (tagName == nsGkAtoms::a) {
1301 nsresult rv =
1302 MOZ_KnownLive(htmlEditor)
1303 ->InsertLinkAroundSelectionAsAction(newElement, aPrincipal);
1304 NS_WARNING_ASSERTION(
1305 NS_SUCCEEDED(rv),
1306 "HTMLEditor::InsertLinkAroundSelectionAsAction() failed");
1307 return rv;
1310 HTMLEditor::InsertElementOptions options{
1311 HTMLEditor::InsertElementOption::DeleteSelection};
1312 // We did insert <img> without splitting ancestor inline elements, but the
1313 // other browsers split them. Therefore, let's do it only when the document
1314 // is content.
1315 if (htmlEditor->GetDocument()->IsContentDocument()) {
1316 options += HTMLEditor::InsertElementOption::SplitAncestorInlineElements;
1318 nsresult rv =
1319 MOZ_KnownLive(htmlEditor)
1320 ->InsertElementAtSelectionAsAction(newElement, options, aPrincipal);
1321 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1322 "HTMLEditor::InsertElementAtSelectionAsAction() failed");
1323 return rv;
1326 nsresult InsertTagCommand::GetCommandStateParams(
1327 Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1328 nsIEditingSession* aEditingSession) const {
1329 return aParams.SetBool(STATE_ENABLED,
1330 IsCommandEnabled(aCommand, aEditorBase));
1333 /*****************************************************************************
1334 * Helper methods
1335 *****************************************************************************/
1337 static nsresult GetListState(HTMLEditor* aHTMLEditor, bool* aMixed,
1338 nsAString& aLocalName) {
1339 MOZ_ASSERT(aHTMLEditor);
1340 MOZ_ASSERT(aMixed);
1342 *aMixed = false;
1343 aLocalName.Truncate();
1345 ErrorResult error;
1346 ListElementSelectionState state(*aHTMLEditor, error);
1347 if (error.Failed()) {
1348 NS_WARNING("ListElementSelectionState failed");
1349 return error.StealNSResult();
1351 if (state.IsNotOneTypeListElementSelected()) {
1352 *aMixed = true;
1353 return NS_OK;
1356 if (state.IsOLElementSelected()) {
1357 aLocalName.AssignLiteral("ol");
1358 } else if (state.IsULElementSelected()) {
1359 aLocalName.AssignLiteral("ul");
1360 } else if (state.IsDLElementSelected()) {
1361 aLocalName.AssignLiteral("dl");
1363 return NS_OK;
1366 } // namespace mozilla