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 "mozilla/EditorCommands.h"
7 
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
25 
26 class nsISupports;
27 
28 namespace mozilla {
29 using dom::Element;
30 
31 // prototype
32 static nsresult GetListState(HTMLEditor* aHTMLEditor, bool* aMixed,
33                              nsAString& aLocalName);
34 
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"
44 
45 /*****************************************************************************
46  * mozilla::StateUpdatingCommandBase
47  *****************************************************************************/
48 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const49 bool StateUpdatingCommandBase::IsCommandEnabled(Command aCommand,
50                                                 EditorBase* aEditorBase) const {
51   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
52   if (!htmlEditor) {
53     return false;
54   }
55   if (!htmlEditor->IsSelectionEditable()) {
56     return false;
57   }
58   if (aCommand == Command::FormatAbsolutePosition) {
59     return htmlEditor->IsAbsolutePositionEditorEnabled();
60   }
61   return true;
62 }
63 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const64 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;
70   }
71   nsStaticAtom* tagName = GetTagName(aCommand);
72   if (NS_WARN_IF(!tagName)) {
73     return NS_ERROR_UNEXPECTED;
74   }
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;
80 }
81 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const82 nsresult StateUpdatingCommandBase::GetCommandStateParams(
83     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
84     nsIEditingSession* aEditingSession) const {
85   if (!aEditorBase) {
86     return NS_OK;
87   }
88   HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor();
89   if (NS_WARN_IF(!htmlEditor)) {
90     return NS_ERROR_FAILURE;
91   }
92   nsAtom* tagName = GetTagName(aCommand);
93   if (NS_WARN_IF(!tagName)) {
94     return NS_ERROR_UNEXPECTED;
95   }
96   nsresult rv = GetCurrentState(MOZ_KnownLive(tagName),
97                                 MOZ_KnownLive(htmlEditor), aParams);
98   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
99                        "StateUpdatingCommandBase::GetCurrentState() failed");
100   return rv;
101 }
102 
103 /*****************************************************************************
104  * mozilla::PasteNoFormattingCommand
105  *****************************************************************************/
106 
107 StaticRefPtr<PasteNoFormattingCommand> PasteNoFormattingCommand::sInstance;
108 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const109 bool PasteNoFormattingCommand::IsCommandEnabled(Command aCommand,
110                                                 EditorBase* aEditorBase) const {
111   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
112   if (!htmlEditor) {
113     return false;
114   }
115   return htmlEditor->CanPaste(nsIClipboard::kGlobalClipboard);
116 }
117 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const118 nsresult PasteNoFormattingCommand::DoCommand(Command aCommand,
119                                              EditorBase& aEditorBase,
120                                              nsIPrincipal* aPrincipal) const {
121   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
122   if (NS_WARN_IF(!htmlEditor)) {
123     return NS_ERROR_FAILURE;
124   }
125   // Known live because we hold a ref above in "editor"
126   nsresult rv = MOZ_KnownLive(htmlEditor)
127                     ->PasteNoFormattingAsAction(nsIClipboard::kGlobalClipboard,
128                                                 aPrincipal);
129   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
130                        "HTMLEditor::PasteNoFormattingAsAction() failed");
131   return rv;
132 }
133 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const134 nsresult PasteNoFormattingCommand::GetCommandStateParams(
135     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
136     nsIEditingSession* aEditingSession) const {
137   return aParams.SetBool(STATE_ENABLED,
138                          IsCommandEnabled(aCommand, aEditorBase));
139 }
140 
141 /*****************************************************************************
142  * mozilla::StyleUpdatingCommand
143  *****************************************************************************/
144 
145 StaticRefPtr<StyleUpdatingCommand> StyleUpdatingCommand::sInstance;
146 
GetCurrentState(nsAtom * aTagName,HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const147 nsresult StyleUpdatingCommand::GetCurrentState(nsAtom* aTagName,
148                                                HTMLEditor* aHTMLEditor,
149                                                nsCommandParams& aParams) const {
150   if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
151     return NS_ERROR_INVALID_ARG;
152   }
153 
154   bool firstOfSelectionHasProp = false;
155   bool anyOfSelectionHasProp = false;
156   bool allOfSelectionHasProp = false;
157 
158   nsresult rv = aHTMLEditor->GetInlineProperty(
159       aTagName, nullptr, u""_ns, &firstOfSelectionHasProp,
160       &anyOfSelectionHasProp, &allOfSelectionHasProp);
161   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
162                        "HTMLEditor::GetInlineProperty() failed");
163 
164   aParams.SetBool(STATE_ENABLED, NS_SUCCEEDED(rv));
165   aParams.SetBool(STATE_ALL, allOfSelectionHasProp);
166   aParams.SetBool(STATE_ANY, anyOfSelectionHasProp);
167   aParams.SetBool(STATE_MIXED, anyOfSelectionHasProp && !allOfSelectionHasProp);
168   aParams.SetBool(STATE_BEGIN, firstOfSelectionHasProp);
169   aParams.SetBool(STATE_END, allOfSelectionHasProp);  // not completely accurate
170   return NS_OK;
171 }
172 
ToggleState(nsStaticAtom & aTagName,HTMLEditor & aHTMLEditor,nsIPrincipal * aPrincipal) const173 nsresult StyleUpdatingCommand::ToggleState(nsStaticAtom& aTagName,
174                                            HTMLEditor& aHTMLEditor,
175                                            nsIPrincipal* aPrincipal) const {
176   RefPtr<nsCommandParams> params = new nsCommandParams();
177 
178   // tags "href" and "name" are special cases in the core editor
179   // they are used to remove named anchor/link and shouldn't be used for
180   // insertion
181   bool doTagRemoval;
182   if (&aTagName == nsGkAtoms::href || &aTagName == nsGkAtoms::name) {
183     doTagRemoval = true;
184   } else {
185     // check current selection; set doTagRemoval if formatting should be removed
186     nsresult rv = GetCurrentState(&aTagName, &aHTMLEditor, *params);
187     if (NS_FAILED(rv)) {
188       NS_WARNING("StyleUpdatingCommand::GetCurrentState() failed");
189       return rv;
190     }
191     ErrorResult error;
192     doTagRemoval = params->GetBool(STATE_ALL, error);
193     if (NS_WARN_IF(error.Failed())) {
194       return error.StealNSResult();
195     }
196   }
197 
198   if (doTagRemoval) {
199     nsresult rv =
200         aHTMLEditor.RemoveInlinePropertyAsAction(aTagName, nullptr, aPrincipal);
201     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
202                          "HTMLEditor::RemoveInlinePropertyAsAction() failed");
203     return rv;
204   }
205 
206   nsresult rv = aHTMLEditor.SetInlinePropertyAsAction(aTagName, nullptr, u""_ns,
207                                                       aPrincipal);
208   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
209                        "HTMLEditor::SetInlinePropertyAsAction() failed");
210   return rv;
211 }
212 
213 /*****************************************************************************
214  * mozilla::ListCommand
215  *****************************************************************************/
216 
217 StaticRefPtr<ListCommand> ListCommand::sInstance;
218 
GetCurrentState(nsAtom * aTagName,HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const219 nsresult ListCommand::GetCurrentState(nsAtom* aTagName, HTMLEditor* aHTMLEditor,
220                                       nsCommandParams& aParams) const {
221   if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
222     return NS_ERROR_INVALID_ARG;
223   }
224 
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;
231   }
232 
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;
238 }
239 
ToggleState(nsStaticAtom & aTagName,HTMLEditor & aHTMLEditor,nsIPrincipal * aPrincipal) const240 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;
248   }
249 
250   ErrorResult error;
251   bool inList = params->GetBool(STATE_ALL, error);
252   if (NS_WARN_IF(error.Failed())) {
253     return error.StealNSResult();
254   }
255 
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;
262   }
263 
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;
269 }
270 
271 /*****************************************************************************
272  * mozilla::ListItemCommand
273  *****************************************************************************/
274 
275 StaticRefPtr<ListItemCommand> ListItemCommand::sInstance;
276 
GetCurrentState(nsAtom * aTagName,HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const277 nsresult ListItemCommand::GetCurrentState(nsAtom* aTagName,
278                                           HTMLEditor* aHTMLEditor,
279                                           nsCommandParams& aParams) const {
280   if (NS_WARN_IF(!aTagName) || NS_WARN_IF(!aHTMLEditor)) {
281     return NS_ERROR_INVALID_ARG;
282   }
283 
284   ErrorResult error;
285   ListItemElementSelectionState state(*aHTMLEditor, error);
286   if (error.Failed()) {
287     NS_WARNING("ListItemElementSelectionState failed");
288     return error.StealNSResult();
289   }
290 
291   if (state.IsNotOneTypeDefinitionListItemElementSelected()) {
292     aParams.SetBool(STATE_ALL, false);
293     aParams.SetBool(STATE_MIXED, true);
294     return NS_OK;
295   }
296 
297   nsStaticAtom* selectedListItemTagName = nullptr;
298   if (state.IsLIElementSelected()) {
299     selectedListItemTagName = nsGkAtoms::li;
300   } else if (state.IsDTElementSelected()) {
301     selectedListItemTagName = nsGkAtoms::dt;
302   } else if (state.IsDDElementSelected()) {
303     selectedListItemTagName = nsGkAtoms::dd;
304   }
305   aParams.SetBool(STATE_ALL, aTagName == selectedListItemTagName);
306   aParams.SetBool(STATE_MIXED, false);
307   return NS_OK;
308 }
309 
ToggleState(nsStaticAtom & aTagName,HTMLEditor & aHTMLEditor,nsIPrincipal * aPrincipal) const310 nsresult ListItemCommand::ToggleState(nsStaticAtom& aTagName,
311                                       HTMLEditor& aHTMLEditor,
312                                       nsIPrincipal* aPrincipal) const {
313   // Need to use aTagName????
314   RefPtr<nsCommandParams> params = new nsCommandParams();
315   GetCurrentState(&aTagName, &aHTMLEditor, *params);
316   ErrorResult error;
317   bool inList = params->GetBool(STATE_ALL, error);
318   if (NS_WARN_IF(error.Failed())) {
319     return error.StealNSResult();
320   }
321 
322   if (inList) {
323     // To remove a list, first get what kind of list we're in
324     bool bMixed;
325     nsAutoString localName;
326     nsresult rv = GetListState(&aHTMLEditor, &bMixed, localName);
327     if (NS_FAILED(rv)) {
328       NS_WARNING("GetListState() failed");
329       return rv;
330     }
331     if (localName.IsEmpty() || bMixed) {
332       return NS_OK;
333     }
334     rv = aHTMLEditor.RemoveListAsAction(localName, aPrincipal);
335     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
336                          "HTMLEditor::RemoveListAsAction() failed");
337     return rv;
338   }
339 
340   // Set to the requested paragraph type
341   // XXX Note: This actually doesn't work for "LI",
342   //    but we currently don't use this for non DL lists anyway.
343   // Problem: won't this replace any current block paragraph style?
344   nsresult rv = aHTMLEditor.SetParagraphFormatAsAction(
345       nsDependentAtomString(&aTagName), aPrincipal);
346   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
347                        "HTMLEditor::SetParagraphFormatAsAction() failed");
348   return rv;
349 }
350 
351 /*****************************************************************************
352  * mozilla::RemoveListCommand
353  *****************************************************************************/
354 
355 StaticRefPtr<RemoveListCommand> RemoveListCommand::sInstance;
356 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const357 bool RemoveListCommand::IsCommandEnabled(Command aCommand,
358                                          EditorBase* aEditorBase) const {
359   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
360   if (!htmlEditor) {
361     return false;
362   }
363 
364   if (!htmlEditor->IsSelectionEditable()) {
365     return false;
366   }
367 
368   // It is enabled if we are in any list type
369   bool bMixed;
370   nsAutoString localName;
371   nsresult rv = GetListState(MOZ_KnownLive(htmlEditor), &bMixed, localName);
372   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetListState() failed");
373   return NS_SUCCEEDED(rv) && (bMixed || !localName.IsEmpty());
374 }
375 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const376 nsresult RemoveListCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
377                                       nsIPrincipal* aPrincipal) const {
378   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
379   if (NS_WARN_IF(!htmlEditor)) {
380     return NS_OK;
381   }
382   // This removes any list type
383   nsresult rv =
384       MOZ_KnownLive(htmlEditor)->RemoveListAsAction(u""_ns, aPrincipal);
385   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
386                        "HTMLEditor::RemoveListAsAction() failed");
387   return rv;
388 }
389 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const390 nsresult RemoveListCommand::GetCommandStateParams(
391     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
392     nsIEditingSession* aEditingSession) const {
393   return aParams.SetBool(STATE_ENABLED,
394                          IsCommandEnabled(aCommand, aEditorBase));
395 }
396 
397 /*****************************************************************************
398  * mozilla::IndentCommand
399  *****************************************************************************/
400 
401 StaticRefPtr<IndentCommand> IndentCommand::sInstance;
402 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const403 bool IndentCommand::IsCommandEnabled(Command aCommand,
404                                      EditorBase* aEditorBase) const {
405   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
406   if (!htmlEditor) {
407     return false;
408   }
409   return htmlEditor->IsSelectionEditable();
410 }
411 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const412 nsresult IndentCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
413                                   nsIPrincipal* aPrincipal) const {
414   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
415   if (NS_WARN_IF(!htmlEditor)) {
416     return NS_OK;
417   }
418   nsresult rv = MOZ_KnownLive(htmlEditor)->IndentAsAction(aPrincipal);
419   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::IndentAsAction() failed");
420   return rv;
421 }
422 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const423 nsresult IndentCommand::GetCommandStateParams(
424     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
425     nsIEditingSession* aEditingSession) const {
426   return aParams.SetBool(STATE_ENABLED,
427                          IsCommandEnabled(aCommand, aEditorBase));
428 }
429 
430 /*****************************************************************************
431  * mozilla::OutdentCommand
432  *****************************************************************************/
433 
434 StaticRefPtr<OutdentCommand> OutdentCommand::sInstance;
435 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const436 bool OutdentCommand::IsCommandEnabled(Command aCommand,
437                                       EditorBase* aEditorBase) const {
438   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
439   if (!htmlEditor) {
440     return false;
441   }
442   return htmlEditor->IsSelectionEditable();
443 }
444 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const445 nsresult OutdentCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
446                                    nsIPrincipal* aPrincipal) const {
447   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
448   if (NS_WARN_IF(!htmlEditor)) {
449     return NS_OK;
450   }
451   nsresult rv = MOZ_KnownLive(htmlEditor)->OutdentAsAction(aPrincipal);
452   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
453                        "HTMLEditor::OutdentAsAction() failed");
454   return rv;
455 }
456 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const457 nsresult OutdentCommand::GetCommandStateParams(
458     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
459     nsIEditingSession* aEditingSession) const {
460   return aParams.SetBool(STATE_ENABLED,
461                          IsCommandEnabled(aCommand, aEditorBase));
462 }
463 
464 /*****************************************************************************
465  * mozilla::MultiStateCommandBase
466  *****************************************************************************/
467 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const468 bool MultiStateCommandBase::IsCommandEnabled(Command aCommand,
469                                              EditorBase* aEditorBase) const {
470   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
471   if (!htmlEditor) {
472     return false;
473   }
474   // should be disabled sometimes, like if the current selection is an image
475   return htmlEditor->IsSelectionEditable();
476 }
477 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const478 nsresult MultiStateCommandBase::DoCommand(Command aCommand,
479                                           EditorBase& aEditorBase,
480                                           nsIPrincipal* aPrincipal) const {
481   NS_WARNING(
482       "who is calling MultiStateCommandBase::DoCommand (no implementation)?");
483   return NS_OK;
484 }
485 
DoCommandParam(Command aCommand,const nsAString & aStringParam,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const486 nsresult MultiStateCommandBase::DoCommandParam(Command aCommand,
487                                                const nsAString& aStringParam,
488                                                EditorBase& aEditorBase,
489                                                nsIPrincipal* aPrincipal) const {
490   NS_WARNING_ASSERTION(aCommand != Command::FormatJustify,
491                        "Command::FormatJustify should be used only for "
492                        "IsCommandEnabled() and GetCommandStateParams()");
493   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
494   if (NS_WARN_IF(!htmlEditor)) {
495     return NS_ERROR_FAILURE;
496   }
497   nsresult rv = SetState(MOZ_KnownLive(htmlEditor), aStringParam, aPrincipal);
498   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
499                        "MultiStateCommandBase::SetState() failed");
500   return rv;
501 }
502 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const503 nsresult MultiStateCommandBase::GetCommandStateParams(
504     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
505     nsIEditingSession* aEditingSession) const {
506   if (!aEditorBase) {
507     return NS_OK;
508   }
509   HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor();
510   if (NS_WARN_IF(!htmlEditor)) {
511     return NS_ERROR_FAILURE;
512   }
513   nsresult rv = GetCurrentState(MOZ_KnownLive(htmlEditor), aParams);
514   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
515                        "MultiStateCommandBase::GetCurrentState() failed");
516   return rv;
517 }
518 
519 /*****************************************************************************
520  * mozilla::ParagraphStateCommand
521  *****************************************************************************/
522 
523 StaticRefPtr<ParagraphStateCommand> ParagraphStateCommand::sInstance;
524 
GetCurrentState(HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const525 nsresult ParagraphStateCommand::GetCurrentState(
526     HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
527   if (NS_WARN_IF(!aHTMLEditor)) {
528     return NS_ERROR_INVALID_ARG;
529   }
530 
531   ErrorResult error;
532   ParagraphStateAtSelection state(*aHTMLEditor, error);
533   if (error.Failed()) {
534     NS_WARNING("ParagraphStateAtSelection failed");
535     return error.StealNSResult();
536   }
537   aParams.SetBool(STATE_MIXED, state.IsMixed());
538   if (NS_WARN_IF(!state.GetFirstParagraphStateAtSelection())) {
539     // XXX This is odd behavior, we should fix this later.
540     aParams.SetCString(STATE_ATTRIBUTE, "x"_ns);
541   } else {
542     nsCString paragraphState;  // Don't use `nsAutoCString` for avoiding copy.
543     state.GetFirstParagraphStateAtSelection()->ToUTF8String(paragraphState);
544     aParams.SetCString(STATE_ATTRIBUTE, paragraphState);
545   }
546   return NS_OK;
547 }
548 
SetState(HTMLEditor * aHTMLEditor,const nsAString & aNewState,nsIPrincipal * aPrincipal) const549 nsresult ParagraphStateCommand::SetState(HTMLEditor* aHTMLEditor,
550                                          const nsAString& aNewState,
551                                          nsIPrincipal* aPrincipal) const {
552   if (NS_WARN_IF(!aHTMLEditor)) {
553     return NS_ERROR_INVALID_ARG;
554   }
555   nsresult rv = aHTMLEditor->SetParagraphFormatAsAction(aNewState, aPrincipal);
556   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
557                        "HTMLEditor::SetParagraphFormatAsAction() failed");
558   return rv;
559 }
560 
561 /*****************************************************************************
562  * mozilla::FontFaceStateCommand
563  *****************************************************************************/
564 
565 StaticRefPtr<FontFaceStateCommand> FontFaceStateCommand::sInstance;
566 
GetCurrentState(HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const567 nsresult FontFaceStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
568                                                nsCommandParams& aParams) const {
569   if (NS_WARN_IF(!aHTMLEditor)) {
570     return NS_ERROR_INVALID_ARG;
571   }
572 
573   nsAutoString outStateString;
574   bool outMixed;
575   nsresult rv = aHTMLEditor->GetFontFaceState(&outMixed, outStateString);
576   if (NS_FAILED(rv)) {
577     NS_WARNING("HTMLEditor::GetFontFaceState() failed");
578     return rv;
579   }
580   aParams.SetBool(STATE_MIXED, outMixed);
581   aParams.SetCString(STATE_ATTRIBUTE, NS_ConvertUTF16toUTF8(outStateString));
582   return NS_OK;
583 }
584 
SetState(HTMLEditor * aHTMLEditor,const nsAString & aNewState,nsIPrincipal * aPrincipal) const585 nsresult FontFaceStateCommand::SetState(HTMLEditor* aHTMLEditor,
586                                         const nsAString& aNewState,
587                                         nsIPrincipal* aPrincipal) const {
588   if (NS_WARN_IF(!aHTMLEditor)) {
589     return NS_ERROR_INVALID_ARG;
590   }
591 
592   if (aNewState.IsEmpty() || aNewState.EqualsLiteral("normal")) {
593     nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
594         *nsGkAtoms::font, nsGkAtoms::face, aPrincipal);
595     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
596                          "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
597                          "font, nsGkAtoms::face) failed");
598     return rv;
599   }
600 
601   nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
602       *nsGkAtoms::font, nsGkAtoms::face, aNewState, aPrincipal);
603   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
604                        "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::font, "
605                        "nsGkAtoms::face) failed");
606   return rv;
607 }
608 
609 /*****************************************************************************
610  * mozilla::FontSizeStateCommand
611  *****************************************************************************/
612 
613 StaticRefPtr<FontSizeStateCommand> FontSizeStateCommand::sInstance;
614 
GetCurrentState(HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const615 nsresult FontSizeStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
616                                                nsCommandParams& aParams) const {
617   if (NS_WARN_IF(!aHTMLEditor)) {
618     return NS_ERROR_INVALID_ARG;
619   }
620 
621   nsAutoString outStateString;
622   bool firstHas, anyHas, allHas;
623   nsresult rv = aHTMLEditor->GetInlinePropertyWithAttrValue(
624       nsGkAtoms::font, nsGkAtoms::size, u""_ns, &firstHas, &anyHas, &allHas,
625       outStateString);
626   if (NS_FAILED(rv)) {
627     NS_WARNING(
628         "HTMLEditor::GetInlinePropertyWithAttrValue(nsGkAtoms::font, "
629         "nsGkAtoms::size) failed");
630     return rv;
631   }
632 
633   nsAutoCString tOutStateString;
634   LossyCopyUTF16toASCII(outStateString, tOutStateString);
635   aParams.SetBool(STATE_MIXED, anyHas && !allHas);
636   aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
637   aParams.SetBool(STATE_ENABLED, true);
638 
639   return NS_OK;
640 }
641 
642 // acceptable values for "aNewState" are:
643 //   -2
644 //   -1
645 //    0
646 //   +1
647 //   +2
648 //   +3
649 //   medium
650 //   normal
SetState(HTMLEditor * aHTMLEditor,const nsAString & aNewState,nsIPrincipal * aPrincipal) const651 nsresult FontSizeStateCommand::SetState(HTMLEditor* aHTMLEditor,
652                                         const nsAString& aNewState,
653                                         nsIPrincipal* aPrincipal) const {
654   if (NS_WARN_IF(!aHTMLEditor)) {
655     return NS_ERROR_INVALID_ARG;
656   }
657 
658   if (!aNewState.IsEmpty() && !aNewState.EqualsLiteral("normal") &&
659       !aNewState.EqualsLiteral("medium")) {
660     nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
661         *nsGkAtoms::font, nsGkAtoms::size, aNewState, aPrincipal);
662     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
663                          "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::"
664                          "font, nsGkAtoms::size) failed");
665     return rv;
666   }
667 
668   // remove any existing font size, big or small
669   nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
670       *nsGkAtoms::font, nsGkAtoms::size, aPrincipal);
671   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
672                        "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
673                        "font, nsGkAtoms::size) failed");
674   return rv;
675 }
676 
677 /*****************************************************************************
678  * mozilla::FontColorStateCommand
679  *****************************************************************************/
680 
681 StaticRefPtr<FontColorStateCommand> FontColorStateCommand::sInstance;
682 
GetCurrentState(HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const683 nsresult FontColorStateCommand::GetCurrentState(
684     HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
685   if (NS_WARN_IF(!aHTMLEditor)) {
686     return NS_ERROR_INVALID_ARG;
687   }
688 
689   bool outMixed;
690   nsAutoString outStateString;
691   nsresult rv = aHTMLEditor->GetFontColorState(&outMixed, outStateString);
692   if (NS_FAILED(rv)) {
693     NS_WARNING("HTMLEditor::GetFontColorState() failed");
694     return rv;
695   }
696 
697   nsAutoCString tOutStateString;
698   LossyCopyUTF16toASCII(outStateString, tOutStateString);
699   aParams.SetBool(STATE_MIXED, outMixed);
700   aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
701   return NS_OK;
702 }
703 
SetState(HTMLEditor * aHTMLEditor,const nsAString & aNewState,nsIPrincipal * aPrincipal) const704 nsresult FontColorStateCommand::SetState(HTMLEditor* aHTMLEditor,
705                                          const nsAString& aNewState,
706                                          nsIPrincipal* aPrincipal) const {
707   if (NS_WARN_IF(!aHTMLEditor)) {
708     return NS_ERROR_INVALID_ARG;
709   }
710 
711   if (aNewState.IsEmpty() || aNewState.EqualsLiteral("normal")) {
712     nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
713         *nsGkAtoms::font, nsGkAtoms::color, aPrincipal);
714     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
715                          "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
716                          "font, nsGkAtoms::color) failed");
717     return rv;
718   }
719 
720   nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
721       *nsGkAtoms::font, nsGkAtoms::color, aNewState, aPrincipal);
722   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
723                        "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::font, "
724                        "nsGkAtoms::color) failed");
725   return rv;
726 }
727 
728 /*****************************************************************************
729  * mozilla::HighlightColorStateCommand
730  *****************************************************************************/
731 
732 StaticRefPtr<HighlightColorStateCommand> HighlightColorStateCommand::sInstance;
733 
GetCurrentState(HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const734 nsresult HighlightColorStateCommand::GetCurrentState(
735     HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
736   if (NS_WARN_IF(!aHTMLEditor)) {
737     return NS_ERROR_INVALID_ARG;
738   }
739 
740   bool outMixed;
741   nsAutoString outStateString;
742   nsresult rv = aHTMLEditor->GetHighlightColorState(&outMixed, outStateString);
743   if (NS_FAILED(rv)) {
744     NS_WARNING("HTMLEditor::GetHighlightColorState() failed");
745     return rv;
746   }
747 
748   nsAutoCString tOutStateString;
749   LossyCopyUTF16toASCII(outStateString, tOutStateString);
750   aParams.SetBool(STATE_MIXED, outMixed);
751   aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
752   return NS_OK;
753 }
754 
SetState(HTMLEditor * aHTMLEditor,const nsAString & aNewState,nsIPrincipal * aPrincipal) const755 nsresult HighlightColorStateCommand::SetState(HTMLEditor* aHTMLEditor,
756                                               const nsAString& aNewState,
757                                               nsIPrincipal* aPrincipal) const {
758   if (NS_WARN_IF(!aHTMLEditor)) {
759     return NS_ERROR_INVALID_ARG;
760   }
761 
762   if (aNewState.IsEmpty() || aNewState.EqualsLiteral("normal")) {
763     nsresult rv = aHTMLEditor->RemoveInlinePropertyAsAction(
764         *nsGkAtoms::font, nsGkAtoms::bgcolor, aPrincipal);
765     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
766                          "HTMLEditor::RemoveInlinePropertyAsAction(nsGkAtoms::"
767                          "font, nsGkAtoms::bgcolor) failed");
768     return rv;
769   }
770 
771   nsresult rv = aHTMLEditor->SetInlinePropertyAsAction(
772       *nsGkAtoms::font, nsGkAtoms::bgcolor, aNewState, aPrincipal);
773   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
774                        "HTMLEditor::SetInlinePropertyAsAction(nsGkAtoms::font, "
775                        "nsGkAtoms::bgcolor) failed");
776   return rv;
777 }
778 
779 /*****************************************************************************
780  * mozilla::BackgroundColorStateCommand
781  *****************************************************************************/
782 
783 StaticRefPtr<BackgroundColorStateCommand>
784     BackgroundColorStateCommand::sInstance;
785 
GetCurrentState(HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const786 nsresult BackgroundColorStateCommand::GetCurrentState(
787     HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
788   if (NS_WARN_IF(!aHTMLEditor)) {
789     return NS_ERROR_INVALID_ARG;
790   }
791 
792   bool outMixed;
793   nsAutoString outStateString;
794   nsresult rv = aHTMLEditor->GetBackgroundColorState(&outMixed, outStateString);
795   if (NS_FAILED(rv)) {
796     NS_WARNING("HTMLEditor::GetBackgroundColorState() failed");
797     return rv;
798   }
799 
800   nsAutoCString tOutStateString;
801   LossyCopyUTF16toASCII(outStateString, tOutStateString);
802   aParams.SetBool(STATE_MIXED, outMixed);
803   aParams.SetCString(STATE_ATTRIBUTE, tOutStateString);
804   return NS_OK;
805 }
806 
SetState(HTMLEditor * aHTMLEditor,const nsAString & aNewState,nsIPrincipal * aPrincipal) const807 nsresult BackgroundColorStateCommand::SetState(HTMLEditor* aHTMLEditor,
808                                                const nsAString& aNewState,
809                                                nsIPrincipal* aPrincipal) const {
810   if (NS_WARN_IF(!aHTMLEditor)) {
811     return NS_ERROR_INVALID_ARG;
812   }
813   nsresult rv = aHTMLEditor->SetBackgroundColorAsAction(aNewState, aPrincipal);
814   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
815                        "HTMLEditor::SetBackgroundColorAsAction() failed");
816   return rv;
817 }
818 
819 /*****************************************************************************
820  * mozilla::AlignCommand
821  *****************************************************************************/
822 
823 StaticRefPtr<AlignCommand> AlignCommand::sInstance;
824 
GetCurrentState(HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const825 nsresult AlignCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
826                                        nsCommandParams& aParams) const {
827   if (NS_WARN_IF(!aHTMLEditor)) {
828     return NS_ERROR_INVALID_ARG;
829   }
830 
831   ErrorResult error;
832   AlignStateAtSelection state(*aHTMLEditor, error);
833   if (error.Failed()) {
834     if (!state.IsSelectionRangesFound()) {
835       // If there was no selection ranges, we shouldn't throw exception for
836       // compatibility with the other browsers, but I have no better idea
837       // than returning empty string in this case.  Oddly, Blink/WebKit returns
838       // "true" or "false", but it's different from us and the value does not
839       // make sense.  Additionally, WPT loves our behavior.
840       error.SuppressException();
841       aParams.SetBool(STATE_MIXED, false);
842       aParams.SetCString(STATE_ATTRIBUTE, ""_ns);
843       return NS_OK;
844     }
845     NS_WARNING("AlignStateAtSelection failed");
846     return error.StealNSResult();
847   }
848   nsCString alignment;  // Don't use `nsAutoCString` to avoid copying string.
849   switch (state.AlignmentAtSelectionStart()) {
850     default:
851     case nsIHTMLEditor::eLeft:
852       alignment.AssignLiteral("left");
853       break;
854     case nsIHTMLEditor::eCenter:
855       alignment.AssignLiteral("center");
856       break;
857     case nsIHTMLEditor::eRight:
858       alignment.AssignLiteral("right");
859       break;
860     case nsIHTMLEditor::eJustify:
861       alignment.AssignLiteral("justify");
862       break;
863   }
864   aParams.SetBool(STATE_MIXED, false);
865   aParams.SetCString(STATE_ATTRIBUTE, alignment);
866   return NS_OK;
867 }
868 
SetState(HTMLEditor * aHTMLEditor,const nsAString & aNewState,nsIPrincipal * aPrincipal) const869 nsresult AlignCommand::SetState(HTMLEditor* aHTMLEditor,
870                                 const nsAString& aNewState,
871                                 nsIPrincipal* aPrincipal) const {
872   if (NS_WARN_IF(!aHTMLEditor)) {
873     return NS_ERROR_INVALID_ARG;
874   }
875   nsresult rv = aHTMLEditor->AlignAsAction(aNewState, aPrincipal);
876   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::AlignAsAction() failed");
877   return rv;
878 }
879 
880 /*****************************************************************************
881  * mozilla::AbsolutePositioningCommand
882  *****************************************************************************/
883 
884 StaticRefPtr<AbsolutePositioningCommand> AbsolutePositioningCommand::sInstance;
885 
GetCurrentState(nsAtom * aTagName,HTMLEditor * aHTMLEditor,nsCommandParams & aParams) const886 nsresult AbsolutePositioningCommand::GetCurrentState(
887     nsAtom* aTagName, HTMLEditor* aHTMLEditor, nsCommandParams& aParams) const {
888   if (NS_WARN_IF(!aHTMLEditor)) {
889     return NS_ERROR_INVALID_ARG;
890   }
891 
892   if (!aHTMLEditor->IsAbsolutePositionEditorEnabled()) {
893     aParams.SetBool(STATE_MIXED, false);
894     aParams.SetCString(STATE_ATTRIBUTE, ""_ns);
895     return NS_OK;
896   }
897 
898   RefPtr<Element> container =
899       aHTMLEditor->GetAbsolutelyPositionedSelectionContainer();
900   aParams.SetBool(STATE_MIXED, false);
901   aParams.SetCString(STATE_ATTRIBUTE, container ? "absolute"_ns : ""_ns);
902   return NS_OK;
903 }
904 
ToggleState(nsStaticAtom & aTagName,HTMLEditor & aHTMLEditor,nsIPrincipal * aPrincipal) const905 nsresult AbsolutePositioningCommand::ToggleState(
906     nsStaticAtom& aTagName, HTMLEditor& aHTMLEditor,
907     nsIPrincipal* aPrincipal) const {
908   RefPtr<Element> container =
909       aHTMLEditor.GetAbsolutelyPositionedSelectionContainer();
910   nsresult rv = aHTMLEditor.SetSelectionToAbsoluteOrStaticAsAction(!container,
911                                                                    aPrincipal);
912   NS_WARNING_ASSERTION(
913       NS_SUCCEEDED(rv),
914       "HTMLEditor::SetSelectionToAbsoluteOrStaticAsAction() failed");
915   return rv;
916 }
917 
918 /*****************************************************************************
919  * mozilla::DecreaseZIndexCommand
920  *****************************************************************************/
921 
922 StaticRefPtr<DecreaseZIndexCommand> DecreaseZIndexCommand::sInstance;
923 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const924 bool DecreaseZIndexCommand::IsCommandEnabled(Command aCommand,
925                                              EditorBase* aEditorBase) const {
926   RefPtr<HTMLEditor> htmlEditor = HTMLEditor::GetFrom(aEditorBase);
927   if (!htmlEditor) {
928     return false;
929   }
930   if (!htmlEditor->IsAbsolutePositionEditorEnabled()) {
931     return false;
932   }
933   RefPtr<Element> positionedElement = htmlEditor->GetPositionedElement();
934   if (!positionedElement) {
935     return false;
936   }
937   return htmlEditor->GetZIndex(*positionedElement) > 0;
938 }
939 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const940 nsresult DecreaseZIndexCommand::DoCommand(Command aCommand,
941                                           EditorBase& aEditorBase,
942                                           nsIPrincipal* aPrincipal) const {
943   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
944   if (NS_WARN_IF(!htmlEditor)) {
945     return NS_ERROR_FAILURE;
946   }
947   nsresult rv = MOZ_KnownLive(htmlEditor)->AddZIndexAsAction(-1, aPrincipal);
948   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
949                        "HTMLEditor::AddZIndexAsAction(-1) failed");
950   return rv;
951 }
952 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const953 nsresult DecreaseZIndexCommand::GetCommandStateParams(
954     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
955     nsIEditingSession* aEditingSession) const {
956   return aParams.SetBool(STATE_ENABLED,
957                          IsCommandEnabled(aCommand, aEditorBase));
958 }
959 
960 /*****************************************************************************
961  * mozilla::IncreaseZIndexCommand
962  *****************************************************************************/
963 
964 StaticRefPtr<IncreaseZIndexCommand> IncreaseZIndexCommand::sInstance;
965 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const966 bool IncreaseZIndexCommand::IsCommandEnabled(Command aCommand,
967                                              EditorBase* aEditorBase) const {
968   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
969   if (!htmlEditor) {
970     return false;
971   }
972   if (!htmlEditor->IsAbsolutePositionEditorEnabled()) {
973     return false;
974   }
975   return !!htmlEditor->GetPositionedElement();
976 }
977 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const978 nsresult IncreaseZIndexCommand::DoCommand(Command aCommand,
979                                           EditorBase& aEditorBase,
980                                           nsIPrincipal* aPrincipal) const {
981   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
982   if (NS_WARN_IF(!htmlEditor)) {
983     return NS_ERROR_FAILURE;
984   }
985   nsresult rv = MOZ_KnownLive(htmlEditor)->AddZIndexAsAction(1, aPrincipal);
986   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
987                        "HTMLEditor::AddZIndexAsAction(1) failed");
988   return rv;
989 }
990 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const991 nsresult IncreaseZIndexCommand::GetCommandStateParams(
992     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
993     nsIEditingSession* aEditingSession) const {
994   return aParams.SetBool(STATE_ENABLED,
995                          IsCommandEnabled(aCommand, aEditorBase));
996 }
997 
998 /*****************************************************************************
999  * mozilla::RemoveStylesCommand
1000  *****************************************************************************/
1001 
1002 StaticRefPtr<RemoveStylesCommand> RemoveStylesCommand::sInstance;
1003 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const1004 bool RemoveStylesCommand::IsCommandEnabled(Command aCommand,
1005                                            EditorBase* aEditorBase) const {
1006   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1007   if (!htmlEditor) {
1008     return false;
1009   }
1010   // test if we have any styles?
1011   return htmlEditor->IsSelectionEditable();
1012 }
1013 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const1014 nsresult RemoveStylesCommand::DoCommand(Command aCommand,
1015                                         EditorBase& aEditorBase,
1016                                         nsIPrincipal* aPrincipal) const {
1017   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1018   if (NS_WARN_IF(!htmlEditor)) {
1019     return NS_OK;
1020   }
1021   nsresult rv =
1022       MOZ_KnownLive(htmlEditor)->RemoveAllInlinePropertiesAsAction(aPrincipal);
1023   NS_WARNING_ASSERTION(
1024       NS_SUCCEEDED(rv),
1025       "HTMLEditor::RemoveAllInlinePropertiesAsAction() failed");
1026   return rv;
1027 }
1028 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const1029 nsresult RemoveStylesCommand::GetCommandStateParams(
1030     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1031     nsIEditingSession* aEditingSession) const {
1032   return aParams.SetBool(STATE_ENABLED,
1033                          IsCommandEnabled(aCommand, aEditorBase));
1034 }
1035 
1036 /*****************************************************************************
1037  * mozilla::IncreaseFontSizeCommand
1038  *****************************************************************************/
1039 
1040 StaticRefPtr<IncreaseFontSizeCommand> IncreaseFontSizeCommand::sInstance;
1041 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const1042 bool IncreaseFontSizeCommand::IsCommandEnabled(Command aCommand,
1043                                                EditorBase* aEditorBase) const {
1044   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1045   if (!htmlEditor) {
1046     return false;
1047   }
1048   // test if we are at max size?
1049   return htmlEditor->IsSelectionEditable();
1050 }
1051 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const1052 nsresult IncreaseFontSizeCommand::DoCommand(Command aCommand,
1053                                             EditorBase& aEditorBase,
1054                                             nsIPrincipal* aPrincipal) const {
1055   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1056   if (NS_WARN_IF(!htmlEditor)) {
1057     return NS_OK;
1058   }
1059   nsresult rv = MOZ_KnownLive(htmlEditor)->IncreaseFontSizeAsAction(aPrincipal);
1060   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1061                        "HTMLEditor::IncreaseFontSizeAsAction() failed");
1062   return rv;
1063 }
1064 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const1065 nsresult IncreaseFontSizeCommand::GetCommandStateParams(
1066     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1067     nsIEditingSession* aEditingSession) const {
1068   return aParams.SetBool(STATE_ENABLED,
1069                          IsCommandEnabled(aCommand, aEditorBase));
1070 }
1071 
1072 /*****************************************************************************
1073  * mozilla::DecreaseFontSizeCommand
1074  *****************************************************************************/
1075 
1076 StaticRefPtr<DecreaseFontSizeCommand> DecreaseFontSizeCommand::sInstance;
1077 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const1078 bool DecreaseFontSizeCommand::IsCommandEnabled(Command aCommand,
1079                                                EditorBase* aEditorBase) const {
1080   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1081   if (!htmlEditor) {
1082     return false;
1083   }
1084   // test if we are at min size?
1085   return htmlEditor->IsSelectionEditable();
1086 }
1087 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const1088 nsresult DecreaseFontSizeCommand::DoCommand(Command aCommand,
1089                                             EditorBase& aEditorBase,
1090                                             nsIPrincipal* aPrincipal) const {
1091   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1092   if (NS_WARN_IF(!htmlEditor)) {
1093     return NS_OK;
1094   }
1095   nsresult rv = MOZ_KnownLive(htmlEditor)->DecreaseFontSizeAsAction(aPrincipal);
1096   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1097                        "HTMLEditor::DecreaseFontSizeAsAction() failed");
1098   return rv;
1099 }
1100 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const1101 nsresult DecreaseFontSizeCommand::GetCommandStateParams(
1102     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1103     nsIEditingSession* aEditingSession) const {
1104   return aParams.SetBool(STATE_ENABLED,
1105                          IsCommandEnabled(aCommand, aEditorBase));
1106 }
1107 
1108 /*****************************************************************************
1109  * mozilla::InsertHTMLCommand
1110  *****************************************************************************/
1111 
1112 StaticRefPtr<InsertHTMLCommand> InsertHTMLCommand::sInstance;
1113 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const1114 bool InsertHTMLCommand::IsCommandEnabled(Command aCommand,
1115                                          EditorBase* aEditorBase) const {
1116   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1117   if (!htmlEditor) {
1118     return false;
1119   }
1120   return htmlEditor->IsSelectionEditable();
1121 }
1122 
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const1123 nsresult InsertHTMLCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
1124                                       nsIPrincipal* aPrincipal) const {
1125   // If InsertHTMLCommand is called with no parameters, it was probably called
1126   // with an empty string parameter ''. In this case, it should act the same as
1127   // the delete command
1128   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1129   if (NS_WARN_IF(!htmlEditor)) {
1130     return NS_ERROR_FAILURE;
1131   }
1132   nsresult rv =
1133       MOZ_KnownLive(htmlEditor)->InsertHTMLAsAction(u""_ns, aPrincipal);
1134   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1135                        "HTMLEditor::InsertHTMLAsAction() failed");
1136   return rv;
1137 }
1138 
DoCommandParam(Command aCommand,const nsAString & aStringParam,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const1139 nsresult InsertHTMLCommand::DoCommandParam(Command aCommand,
1140                                            const nsAString& aStringParam,
1141                                            EditorBase& aEditorBase,
1142                                            nsIPrincipal* aPrincipal) const {
1143   if (NS_WARN_IF(aStringParam.IsVoid())) {
1144     return NS_ERROR_INVALID_ARG;
1145   }
1146 
1147   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1148   if (NS_WARN_IF(!htmlEditor)) {
1149     return NS_ERROR_FAILURE;
1150   }
1151   nsresult rv =
1152       MOZ_KnownLive(htmlEditor)->InsertHTMLAsAction(aStringParam, aPrincipal);
1153   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1154                        "HTMLEditor::InsertHTMLAsAction() failed");
1155   return rv;
1156 }
1157 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const1158 nsresult InsertHTMLCommand::GetCommandStateParams(
1159     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1160     nsIEditingSession* aEditingSession) const {
1161   return aParams.SetBool(STATE_ENABLED,
1162                          IsCommandEnabled(aCommand, aEditorBase));
1163 }
1164 
1165 /*****************************************************************************
1166  * mozilla::InsertTagCommand
1167  *****************************************************************************/
1168 
1169 StaticRefPtr<InsertTagCommand> InsertTagCommand::sInstance;
1170 
IsCommandEnabled(Command aCommand,EditorBase * aEditorBase) const1171 bool InsertTagCommand::IsCommandEnabled(Command aCommand,
1172                                         EditorBase* aEditorBase) const {
1173   HTMLEditor* htmlEditor = HTMLEditor::GetFrom(aEditorBase);
1174   if (!htmlEditor) {
1175     return false;
1176   }
1177   return htmlEditor->IsSelectionEditable();
1178 }
1179 
1180 // corresponding STATE_ATTRIBUTE is: src (img) and href (a)
DoCommand(Command aCommand,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const1181 nsresult InsertTagCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
1182                                      nsIPrincipal* aPrincipal) const {
1183   nsAtom* tagName = GetTagName(aCommand);
1184   if (NS_WARN_IF(tagName != nsGkAtoms::hr)) {
1185     return NS_ERROR_NOT_IMPLEMENTED;
1186   }
1187 
1188   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1189   if (NS_WARN_IF(!htmlEditor)) {
1190     return NS_ERROR_FAILURE;
1191   }
1192 
1193   RefPtr<Element> newElement =
1194       MOZ_KnownLive(htmlEditor)
1195           ->CreateElementWithDefaults(MOZ_KnownLive(*tagName));
1196   if (NS_WARN_IF(!newElement)) {
1197     return NS_ERROR_FAILURE;
1198   }
1199   nsresult rv =
1200       MOZ_KnownLive(htmlEditor)
1201           ->InsertElementAtSelectionAsAction(newElement, true, aPrincipal);
1202   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1203                        "HTMLEditor::InsertElementAtSelectionAsAction() failed");
1204   return rv;
1205 }
1206 
DoCommandParam(Command aCommand,const nsAString & aStringParam,EditorBase & aEditorBase,nsIPrincipal * aPrincipal) const1207 nsresult InsertTagCommand::DoCommandParam(Command aCommand,
1208                                           const nsAString& aStringParam,
1209                                           EditorBase& aEditorBase,
1210                                           nsIPrincipal* aPrincipal) const {
1211   MOZ_ASSERT(aCommand != Command::InsertHorizontalRule);
1212 
1213   if (NS_WARN_IF(aStringParam.IsEmpty())) {
1214     return NS_ERROR_INVALID_ARG;
1215   }
1216   nsAtom* tagName = GetTagName(aCommand);
1217   if (NS_WARN_IF(!tagName)) {
1218     return NS_ERROR_UNEXPECTED;
1219   }
1220 
1221   HTMLEditor* htmlEditor = aEditorBase.GetAsHTMLEditor();
1222   if (NS_WARN_IF(!htmlEditor)) {
1223     return NS_ERROR_FAILURE;
1224   }
1225 
1226   // filter out tags we don't know how to insert
1227   nsAtom* attribute = nullptr;
1228   if (tagName == nsGkAtoms::a) {
1229     attribute = nsGkAtoms::href;
1230   } else if (tagName == nsGkAtoms::img) {
1231     attribute = nsGkAtoms::src;
1232   } else {
1233     return NS_ERROR_NOT_IMPLEMENTED;
1234   }
1235 
1236   RefPtr<Element> newElement =
1237       MOZ_KnownLive(htmlEditor)
1238           ->CreateElementWithDefaults(MOZ_KnownLive(*tagName));
1239   if (!newElement) {
1240     NS_WARNING("HTMLEditor::CreateElementWithDefaults() failed");
1241     return NS_ERROR_FAILURE;
1242   }
1243 
1244   ErrorResult error;
1245   newElement->SetAttr(attribute, aStringParam, error);
1246   if (error.Failed()) {
1247     NS_WARNING("Element::SetAttr() failed");
1248     return error.StealNSResult();
1249   }
1250 
1251   // do actual insertion
1252   if (tagName == nsGkAtoms::a) {
1253     nsresult rv =
1254         MOZ_KnownLive(htmlEditor)
1255             ->InsertLinkAroundSelectionAsAction(newElement, aPrincipal);
1256     NS_WARNING_ASSERTION(
1257         NS_SUCCEEDED(rv),
1258         "HTMLEditor::InsertLinkAroundSelectionAsAction() failed");
1259     return rv;
1260   }
1261 
1262   nsresult rv =
1263       MOZ_KnownLive(htmlEditor)
1264           ->InsertElementAtSelectionAsAction(newElement, true, aPrincipal);
1265   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1266                        "HTMLEditor::InsertElementAtSelectionAsAction() failed");
1267   return rv;
1268 }
1269 
GetCommandStateParams(Command aCommand,nsCommandParams & aParams,EditorBase * aEditorBase,nsIEditingSession * aEditingSession) const1270 nsresult InsertTagCommand::GetCommandStateParams(
1271     Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
1272     nsIEditingSession* aEditingSession) const {
1273   return aParams.SetBool(STATE_ENABLED,
1274                          IsCommandEnabled(aCommand, aEditorBase));
1275 }
1276 
1277 /*****************************************************************************
1278  * Helper methods
1279  *****************************************************************************/
1280 
GetListState(HTMLEditor * aHTMLEditor,bool * aMixed,nsAString & aLocalName)1281 static nsresult GetListState(HTMLEditor* aHTMLEditor, bool* aMixed,
1282                              nsAString& aLocalName) {
1283   MOZ_ASSERT(aHTMLEditor);
1284   MOZ_ASSERT(aMixed);
1285 
1286   *aMixed = false;
1287   aLocalName.Truncate();
1288 
1289   ErrorResult error;
1290   ListElementSelectionState state(*aHTMLEditor, error);
1291   if (error.Failed()) {
1292     NS_WARNING("ListElementSelectionState failed");
1293     return error.StealNSResult();
1294   }
1295   if (state.IsNotOneTypeListElementSelected()) {
1296     *aMixed = true;
1297     return NS_OK;
1298   }
1299 
1300   if (state.IsOLElementSelected()) {
1301     aLocalName.AssignLiteral("ol");
1302   } else if (state.IsULElementSelected()) {
1303     aLocalName.AssignLiteral("ul");
1304   } else if (state.IsDLElementSelected()) {
1305     aLocalName.AssignLiteral("dl");
1306   }
1307   return NS_OK;
1308 }
1309 
1310 }  // namespace mozilla
1311