tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EditorCommands.cpp (38089B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "EditorCommands.h"
      7 
      8 #include "mozilla/Assertions.h"
      9 #include "mozilla/EditorBase.h"
     10 #include "mozilla/FlushType.h"
     11 #include "mozilla/HTMLEditor.h"
     12 #include "mozilla/Maybe.h"
     13 #include "mozilla/MozPromise.h"  // for mozilla::detail::Any
     14 #include "mozilla/dom/DataTransfer.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/Selection.h"
     17 #include "nsCommandParams.h"
     18 #include "nsIClipboard.h"
     19 #include "nsIEditingSession.h"
     20 #include "nsIPrincipal.h"
     21 #include "nsISelectionController.h"
     22 #include "nsITransferable.h"
     23 #include "nsString.h"
     24 #include "nsAString.h"
     25 
     26 class nsISupports;
     27 
     28 #define STATE_ENABLED "state_enabled"
     29 #define STATE_ATTRIBUTE "state_attribute"
     30 #define STATE_DATA "state_data"
     31 
     32 namespace mozilla {
     33 
     34 using detail::Any;
     35 
     36 /******************************************************************************
     37 * mozilla::EditorCommand
     38 ******************************************************************************/
     39 
     40 bool EditorCommand::IsCommandEnabled(const nsACString& aCommandName,
     41                                     nsISupports* aCommandRefCon) {
     42  nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
     43  EditorBase* editorBase = editor ? editor->AsEditorBase() : nullptr;
     44  return IsCommandEnabled(GetInternalCommand(aCommandName),
     45                          MOZ_KnownLive(editorBase));
     46 }
     47 
     48 nsresult EditorCommand::DoCommand(const nsACString& aCommandName,
     49                                  nsICommandParams* aParams,
     50                                  nsISupports* aCommandRefCon) {
     51  if (NS_WARN_IF(!aCommandRefCon)) {
     52    return NS_ERROR_INVALID_ARG;
     53  }
     54  nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
     55  if (NS_WARN_IF(!editor)) {
     56    return NS_ERROR_INVALID_ARG;
     57  }
     58  nsCommandParams* params = aParams ? aParams->AsCommandParams() : nullptr;
     59  Command command = GetInternalCommand(aCommandName, params);
     60  EditorCommandParamType paramType = EditorCommand::GetParamType(command);
     61  if (paramType == EditorCommandParamType::None) {
     62    nsresult rv = DoCommandParam(
     63        command, MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
     64    NS_WARNING_ASSERTION(
     65        NS_SUCCEEDED(rv),
     66        "Failed to do command from nsIControllerCommand::DoCommandParams()");
     67    return rv;
     68  }
     69 
     70  if (Any(paramType & EditorCommandParamType::Bool)) {
     71    if (Any(paramType & EditorCommandParamType::StateAttribute)) {
     72      Maybe<bool> boolParam = Nothing();
     73      if (params) {
     74        ErrorResult error;
     75        boolParam = Some(params->GetBool(STATE_ATTRIBUTE, error));
     76        if (NS_WARN_IF(error.Failed())) {
     77          return error.StealNSResult();
     78        }
     79      }
     80      nsresult rv = DoCommandParam(
     81          command, boolParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
     82      NS_WARNING_ASSERTION(
     83          NS_SUCCEEDED(rv),
     84          "Failed to do command from nsIControllerCommand::DoCommandParams()");
     85      return rv;
     86    }
     87    MOZ_ASSERT_UNREACHABLE("Unexpected state for bool");
     88    return NS_ERROR_NOT_IMPLEMENTED;
     89  }
     90 
     91  // Special case for MultiStateCommandBase.  It allows both CString and String
     92  // in STATE_ATTRIBUTE and CString is preferred.
     93  if (Any(paramType & EditorCommandParamType::CString) &&
     94      Any(paramType & EditorCommandParamType::String)) {
     95    if (!params) {
     96      nsresult rv =
     97          DoCommandParam(command, VoidString(),
     98                         MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
     99      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    100                           "Failed to do command from "
    101                           "nsIControllerCommand::DoCommandParams()");
    102      return rv;
    103    }
    104    if (Any(paramType & EditorCommandParamType::StateAttribute)) {
    105      nsCString cStringParam;
    106      nsresult rv = params->GetCString(STATE_ATTRIBUTE, cStringParam);
    107      if (NS_SUCCEEDED(rv)) {
    108        NS_ConvertUTF8toUTF16 stringParam(cStringParam);
    109        nsresult rv =
    110            DoCommandParam(command, stringParam,
    111                           MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
    112        NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    113                             "Failed to do command from "
    114                             "nsIControllerCommand::DoCommandParams()");
    115        return rv;
    116      }
    117      nsString stringParam;
    118      DebugOnly<nsresult> rvIgnored =
    119          params->GetString(STATE_ATTRIBUTE, stringParam);
    120      NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    121                           "Failed to get string from STATE_ATTRIBUTE");
    122      rv = DoCommandParam(command, stringParam,
    123                          MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
    124      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    125                           "Failed to do command from "
    126                           "nsIControllerCommand::DoCommandParams()");
    127      return rv;
    128    }
    129    MOZ_ASSERT_UNREACHABLE("Unexpected state for CString/String");
    130    return NS_ERROR_NOT_IMPLEMENTED;
    131  }
    132 
    133  if (Any(paramType & EditorCommandParamType::CString)) {
    134    if (!params) {
    135      nsresult rv =
    136          DoCommandParam(command, VoidCString(),
    137                         MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
    138      NS_WARNING_ASSERTION(
    139          NS_SUCCEEDED(rv),
    140          "Failed to do command from nsIControllerCommand::DoCommandParams()");
    141      return rv;
    142    }
    143    if (Any(paramType & EditorCommandParamType::StateAttribute)) {
    144      nsCString cStringParam;
    145      nsresult rv = params->GetCString(STATE_ATTRIBUTE, cStringParam);
    146      if (NS_WARN_IF(NS_FAILED(rv))) {
    147        return rv;
    148      }
    149      rv = DoCommandParam(command, cStringParam,
    150                          MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
    151      NS_WARNING_ASSERTION(
    152          NS_SUCCEEDED(rv),
    153          "Failed to do command from nsIControllerCommand::DoCommandParams()");
    154      return rv;
    155    }
    156    MOZ_ASSERT_UNREACHABLE("Unexpected state for CString");
    157    return NS_ERROR_NOT_IMPLEMENTED;
    158  }
    159 
    160  if (Any(paramType & EditorCommandParamType::String)) {
    161    if (!params) {
    162      nsresult rv =
    163          DoCommandParam(command, VoidString(),
    164                         MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
    165      NS_WARNING_ASSERTION(
    166          NS_SUCCEEDED(rv),
    167          "Failed to do command from nsIControllerCommand::DoCommandParams()");
    168      return rv;
    169    }
    170    nsString stringParam;
    171    if (Any(paramType & EditorCommandParamType::StateAttribute)) {
    172      nsresult rv = params->GetString(STATE_ATTRIBUTE, stringParam);
    173      if (NS_WARN_IF(NS_FAILED(rv))) {
    174        return rv;
    175      }
    176    } else if (Any(paramType & EditorCommandParamType::StateData)) {
    177      nsresult rv = params->GetString(STATE_DATA, stringParam);
    178      if (NS_WARN_IF(NS_FAILED(rv))) {
    179        return rv;
    180      }
    181    } else {
    182      MOZ_ASSERT_UNREACHABLE("Unexpected state for String");
    183      return NS_ERROR_NOT_IMPLEMENTED;
    184    }
    185    nsresult rv = DoCommandParam(
    186        command, stringParam, MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
    187    NS_WARNING_ASSERTION(
    188        NS_SUCCEEDED(rv),
    189        "Failed to do command from nsIControllerCommand::DoCommandParams()");
    190    return rv;
    191  }
    192 
    193  if (Any(paramType & EditorCommandParamType::Transferable)) {
    194    nsCOMPtr<nsITransferable> transferable;
    195    if (params) {
    196      nsCOMPtr<nsISupports> supports = params->GetISupports("transferable");
    197      transferable = do_QueryInterface(supports);
    198    }
    199    nsresult rv = DoCommandParam(
    200        command, transferable, MOZ_KnownLive(*editor->AsEditorBase()), nullptr);
    201    NS_WARNING_ASSERTION(
    202        NS_SUCCEEDED(rv),
    203        "Failed to do command from nsIControllerCommand::DoCommandParams()");
    204    return rv;
    205  }
    206 
    207  MOZ_ASSERT_UNREACHABLE("Unexpected param type");
    208  return NS_ERROR_NOT_IMPLEMENTED;
    209 }
    210 
    211 void EditorCommand::GetCommandStateParams(const nsACString& aCommandName,
    212                                          nsICommandParams* aParams,
    213                                          nsISupports* aCommandRefCon) {
    214  MOZ_ASSERT(aParams);
    215  if (nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon)) {
    216    GetCommandStateParams(GetInternalCommand(aCommandName),
    217                          MOZ_KnownLive(*aParams->AsCommandParams()),
    218                          MOZ_KnownLive(editor->AsEditorBase()), nullptr);
    219    return;
    220  }
    221  nsCOMPtr<nsIEditingSession> editingSession =
    222      do_QueryInterface(aCommandRefCon);
    223  GetCommandStateParams(GetInternalCommand(aCommandName),
    224                        MOZ_KnownLive(*aParams->AsCommandParams()), nullptr,
    225                        editingSession);
    226 }
    227 
    228 /******************************************************************************
    229 * mozilla::UndoCommand
    230 ******************************************************************************/
    231 
    232 StaticRefPtr<UndoCommand> UndoCommand::sInstance;
    233 
    234 bool UndoCommand::IsCommandEnabled(Command aCommand,
    235                                   EditorBase* aEditorBase) const {
    236  if (!aEditorBase) {
    237    return false;
    238  }
    239  return aEditorBase->IsModifiable() && aEditorBase->IsSelectionEditable() &&
    240         aEditorBase->CanUndo();
    241 }
    242 
    243 nsresult UndoCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
    244                                nsIPrincipal* aPrincipal) const {
    245  nsresult rv = aEditorBase.UndoAsAction(1, aPrincipal);
    246  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::UndoAsAction() failed");
    247  return rv;
    248 }
    249 
    250 nsresult UndoCommand::GetCommandStateParams(
    251    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    252    nsIEditingSession* aEditingSession) const {
    253  return aParams.SetBool(STATE_ENABLED,
    254                         IsCommandEnabled(aCommand, aEditorBase));
    255 }
    256 
    257 /******************************************************************************
    258 * mozilla::RedoCommand
    259 ******************************************************************************/
    260 
    261 StaticRefPtr<RedoCommand> RedoCommand::sInstance;
    262 
    263 bool RedoCommand::IsCommandEnabled(Command aCommand,
    264                                   EditorBase* aEditorBase) const {
    265  if (!aEditorBase) {
    266    return false;
    267  }
    268  return aEditorBase->IsModifiable() && aEditorBase->IsSelectionEditable() &&
    269         aEditorBase->CanRedo();
    270 }
    271 
    272 nsresult RedoCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
    273                                nsIPrincipal* aPrincipal) const {
    274  nsresult rv = aEditorBase.RedoAsAction(1, aPrincipal);
    275  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::RedoAsAction() failed");
    276  return rv;
    277 }
    278 
    279 nsresult RedoCommand::GetCommandStateParams(
    280    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    281    nsIEditingSession* aEditingSession) const {
    282  return aParams.SetBool(STATE_ENABLED,
    283                         IsCommandEnabled(aCommand, aEditorBase));
    284 }
    285 
    286 /******************************************************************************
    287 * mozilla::CutCommand
    288 ******************************************************************************/
    289 
    290 StaticRefPtr<CutCommand> CutCommand::sInstance;
    291 
    292 bool CutCommand::IsCommandEnabled(Command aCommand,
    293                                  EditorBase* aEditorBase) const {
    294  if (!aEditorBase) {
    295    return false;
    296  }
    297  return aEditorBase->IsSelectionEditable() &&
    298         aEditorBase->IsCutCommandEnabled();
    299 }
    300 
    301 nsresult CutCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
    302                               nsIPrincipal* aPrincipal) const {
    303  nsresult rv = aEditorBase.CutAsAction(aPrincipal);
    304  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed");
    305  return rv;
    306 }
    307 
    308 nsresult CutCommand::GetCommandStateParams(
    309    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    310    nsIEditingSession* aEditingSession) const {
    311  return aParams.SetBool(STATE_ENABLED,
    312                         IsCommandEnabled(aCommand, aEditorBase));
    313 }
    314 
    315 /******************************************************************************
    316 * mozilla::CutOrDeleteCommand
    317 ******************************************************************************/
    318 
    319 StaticRefPtr<CutOrDeleteCommand> CutOrDeleteCommand::sInstance;
    320 
    321 bool CutOrDeleteCommand::IsCommandEnabled(Command aCommand,
    322                                          EditorBase* aEditorBase) const {
    323  if (!aEditorBase) {
    324    return false;
    325  }
    326  return aEditorBase->IsSelectionEditable();
    327 }
    328 
    329 nsresult CutOrDeleteCommand::DoCommand(Command aCommand,
    330                                       EditorBase& aEditorBase,
    331                                       nsIPrincipal* aPrincipal) const {
    332  dom::Selection* selection = aEditorBase.GetSelection();
    333  if (selection && selection->IsCollapsed()) {
    334    nsresult rv = aEditorBase.DeleteSelectionAsAction(
    335        nsIEditor::eNext, nsIEditor::eStrip, aPrincipal);
    336    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    337                         "EditorBase::DeleteSelectionAsAction() failed");
    338    return rv;
    339  }
    340  nsresult rv = aEditorBase.CutAsAction(aPrincipal);
    341  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed");
    342  return rv;
    343 }
    344 
    345 nsresult CutOrDeleteCommand::GetCommandStateParams(
    346    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    347    nsIEditingSession* aEditingSession) const {
    348  return aParams.SetBool(STATE_ENABLED,
    349                         IsCommandEnabled(aCommand, aEditorBase));
    350 }
    351 
    352 /******************************************************************************
    353 * mozilla::CopyCommand
    354 ******************************************************************************/
    355 
    356 StaticRefPtr<CopyCommand> CopyCommand::sInstance;
    357 
    358 bool CopyCommand::IsCommandEnabled(Command aCommand,
    359                                   EditorBase* aEditorBase) const {
    360  if (!aEditorBase) {
    361    return false;
    362  }
    363  return aEditorBase->IsCopyCommandEnabled();
    364 }
    365 
    366 nsresult CopyCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
    367                                nsIPrincipal* aPrincipal) const {
    368  // Shouldn't cause "beforeinput" event so that we don't need to specify
    369  // the given principal.
    370  return aEditorBase.Copy();
    371 }
    372 
    373 nsresult CopyCommand::GetCommandStateParams(
    374    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    375    nsIEditingSession* aEditingSession) const {
    376  return aParams.SetBool(STATE_ENABLED,
    377                         IsCommandEnabled(aCommand, aEditorBase));
    378 }
    379 
    380 /******************************************************************************
    381 * mozilla::CopyOrDeleteCommand
    382 ******************************************************************************/
    383 
    384 StaticRefPtr<CopyOrDeleteCommand> CopyOrDeleteCommand::sInstance;
    385 
    386 bool CopyOrDeleteCommand::IsCommandEnabled(Command aCommand,
    387                                           EditorBase* aEditorBase) const {
    388  if (!aEditorBase) {
    389    return false;
    390  }
    391  return aEditorBase->IsSelectionEditable();
    392 }
    393 
    394 nsresult CopyOrDeleteCommand::DoCommand(Command aCommand,
    395                                        EditorBase& aEditorBase,
    396                                        nsIPrincipal* aPrincipal) const {
    397  dom::Selection* selection = aEditorBase.GetSelection();
    398  if (selection && selection->IsCollapsed()) {
    399    nsresult rv = aEditorBase.DeleteSelectionAsAction(
    400        nsIEditor::eNextWord, nsIEditor::eStrip, aPrincipal);
    401    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    402                         "EditorBase::DeleteSelectionAsAction() failed");
    403    return rv;
    404  }
    405  // Shouldn't cause "beforeinput" event so that we don't need to specify
    406  // the given principal.
    407  nsresult rv = aEditorBase.Copy();
    408  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::Copy() failed");
    409  return rv;
    410 }
    411 
    412 nsresult CopyOrDeleteCommand::GetCommandStateParams(
    413    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    414    nsIEditingSession* aEditingSession) const {
    415  return aParams.SetBool(STATE_ENABLED,
    416                         IsCommandEnabled(aCommand, aEditorBase));
    417 }
    418 
    419 /******************************************************************************
    420 * mozilla::PasteCommand
    421 ******************************************************************************/
    422 
    423 StaticRefPtr<PasteCommand> PasteCommand::sInstance;
    424 
    425 bool PasteCommand::IsCommandEnabled(Command aCommand,
    426                                    EditorBase* aEditorBase) const {
    427  if (!aEditorBase) {
    428    return false;
    429  }
    430  return aEditorBase->IsSelectionEditable() &&
    431         aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard);
    432 }
    433 
    434 nsresult PasteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
    435                                 nsIPrincipal* aPrincipal) const {
    436  RefPtr<dom::DataTransfer> dataTransfer;
    437  nsCOMPtr<nsIPrincipal> subjectPrincipal =
    438      aPrincipal ? aPrincipal
    439                 : nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
    440  MOZ_ASSERT(subjectPrincipal);
    441 
    442  // If we don't need to get user confirmation for clipboard access, we could
    443  // just let EditorBase::PasteAsAction() to create DataTransfer instance
    444  // synchronously for paste event. Otherwise, we need to spin the event loop to
    445  // wait for the clipboard paste contextmenu to be shown and get user
    446  // confirmation which are all handled in parent process before sending the
    447  // paste event.
    448  if (!nsContentUtils::PrincipalHasPermission(*subjectPrincipal,
    449                                              nsGkAtoms::clipboardRead)) {
    450    MOZ_DIAGNOSTIC_ASSERT(StaticPrefs::dom_execCommand_paste_enabled(),
    451                          "How did we get here?");
    452    // This will spin the event loop.
    453    nsCOMPtr<nsPIDOMWindowOuter> window = aEditorBase.GetWindow();
    454    dataTransfer = dom::DataTransfer::WaitForClipboardDataSnapshotAndCreate(
    455        window, subjectPrincipal);
    456    if (!dataTransfer) {
    457      return NS_SUCCESS_DOM_NO_OPERATION;
    458    }
    459  }
    460 
    461  nsresult rv = aEditorBase.PasteAsAction(nsIClipboard::kGlobalClipboard,
    462                                          EditorBase::DispatchPasteEvent::Yes,
    463                                          dataTransfer, aPrincipal);
    464  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    465                       "EditorBase::PasteAsAction(nsIClipboard::"
    466                       "kGlobalClipboard, DispatchPasteEvent::Yes) failed");
    467  return rv;
    468 }
    469 
    470 nsresult PasteCommand::GetCommandStateParams(
    471    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    472    nsIEditingSession* aEditingSession) const {
    473  return aParams.SetBool(STATE_ENABLED,
    474                         IsCommandEnabled(aCommand, aEditorBase));
    475 }
    476 
    477 /******************************************************************************
    478 * mozilla::PasteTransferableCommand
    479 ******************************************************************************/
    480 
    481 StaticRefPtr<PasteTransferableCommand> PasteTransferableCommand::sInstance;
    482 
    483 bool PasteTransferableCommand::IsCommandEnabled(Command aCommand,
    484                                                EditorBase* aEditorBase) const {
    485  if (!aEditorBase) {
    486    return false;
    487  }
    488  return aEditorBase->IsSelectionEditable() &&
    489         aEditorBase->CanPasteTransferable(nullptr);
    490 }
    491 
    492 nsresult PasteTransferableCommand::DoCommand(Command aCommand,
    493                                             EditorBase& aEditorBase,
    494                                             nsIPrincipal* aPrincipal) const {
    495  return NS_ERROR_FAILURE;
    496 }
    497 
    498 nsresult PasteTransferableCommand::DoCommandParam(
    499    Command aCommand, nsITransferable* aTransferableParam,
    500    EditorBase& aEditorBase, nsIPrincipal* aPrincipal) const {
    501  if (NS_WARN_IF(!aTransferableParam)) {
    502    return NS_ERROR_INVALID_ARG;
    503  }
    504  nsresult rv = aEditorBase.PasteTransferableAsAction(
    505      aTransferableParam, EditorBase::DispatchPasteEvent::Yes, aPrincipal);
    506  NS_WARNING_ASSERTION(
    507      NS_SUCCEEDED(rv),
    508      "EditorBase::PasteTransferableAsAction(DispatchPasteEvent::Yes) failed");
    509  return rv;
    510 }
    511 
    512 nsresult PasteTransferableCommand::GetCommandStateParams(
    513    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    514    nsIEditingSession* aEditingSession) const {
    515  if (NS_WARN_IF(!aEditorBase)) {
    516    return NS_ERROR_INVALID_ARG;
    517  }
    518 
    519  nsCOMPtr<nsISupports> supports = aParams.GetISupports("transferable");
    520  if (NS_WARN_IF(!supports)) {
    521    return NS_ERROR_FAILURE;
    522  }
    523 
    524  nsCOMPtr<nsITransferable> trans;
    525  trans = do_QueryInterface(supports);
    526  if (NS_WARN_IF(!trans)) {
    527    return NS_ERROR_FAILURE;
    528  }
    529 
    530  return aParams.SetBool(STATE_ENABLED,
    531                         aEditorBase->CanPasteTransferable(trans));
    532 }
    533 
    534 /******************************************************************************
    535 * mozilla::SwitchTextDirectionCommand
    536 ******************************************************************************/
    537 
    538 StaticRefPtr<SwitchTextDirectionCommand> SwitchTextDirectionCommand::sInstance;
    539 
    540 bool SwitchTextDirectionCommand::IsCommandEnabled(
    541    Command aCommand, EditorBase* aEditorBase) const {
    542  if (!aEditorBase) {
    543    return false;
    544  }
    545  return aEditorBase->IsModifiable() && aEditorBase->IsSelectionEditable();
    546 }
    547 
    548 nsresult SwitchTextDirectionCommand::DoCommand(Command aCommand,
    549                                               EditorBase& aEditorBase,
    550                                               nsIPrincipal* aPrincipal) const {
    551  nsresult rv = aEditorBase.ToggleTextDirectionAsAction(aPrincipal);
    552  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    553                       "EditorBase::ToggleTextDirectionAsAction() failed");
    554  return rv;
    555 }
    556 
    557 nsresult SwitchTextDirectionCommand::GetCommandStateParams(
    558    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    559    nsIEditingSession* aEditingSession) const {
    560  return aParams.SetBool(STATE_ENABLED,
    561                         IsCommandEnabled(aCommand, aEditorBase));
    562 }
    563 
    564 /******************************************************************************
    565 * mozilla::DeleteCommand
    566 ******************************************************************************/
    567 
    568 StaticRefPtr<DeleteCommand> DeleteCommand::sInstance;
    569 
    570 bool DeleteCommand::IsCommandEnabled(Command aCommand,
    571                                     EditorBase* aEditorBase) const {
    572  if (!aEditorBase) {
    573    return false;
    574  }
    575  // We can generally delete whenever the selection is editable.  However,
    576  // cmd_delete doesn't make sense if the selection is collapsed because it's
    577  // directionless.
    578  bool isEnabled =
    579      aEditorBase->IsModifiable() && aEditorBase->IsSelectionEditable();
    580 
    581  if (aCommand == Command::Delete && isEnabled) {
    582    return aEditorBase->CanDeleteSelection();
    583  }
    584  return isEnabled;
    585 }
    586 
    587 nsresult DeleteCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
    588                                  nsIPrincipal* aPrincipal) const {
    589  nsIEditor::EDirection deleteDir = nsIEditor::eNone;
    590  switch (aCommand) {
    591    case Command::Delete:
    592      // Really this should probably be eNone, but it only makes a difference
    593      // if the selection is collapsed, and then this command is disabled.  So
    594      // let's keep it as it always was to avoid breaking things.
    595      deleteDir = nsIEditor::ePrevious;
    596      break;
    597    case Command::DeleteCharForward:
    598      deleteDir = nsIEditor::eNext;
    599      break;
    600    case Command::DeleteCharBackward:
    601      deleteDir = nsIEditor::ePrevious;
    602      break;
    603    case Command::DeleteWordBackward:
    604      deleteDir = nsIEditor::ePreviousWord;
    605      break;
    606    case Command::DeleteWordForward:
    607      deleteDir = nsIEditor::eNextWord;
    608      break;
    609    case Command::DeleteToBeginningOfLine:
    610      deleteDir = nsIEditor::eToBeginningOfLine;
    611      break;
    612    case Command::DeleteToEndOfLine:
    613      deleteDir = nsIEditor::eToEndOfLine;
    614      break;
    615    default:
    616      MOZ_CRASH("Unrecognized nsDeleteCommand");
    617  }
    618  nsresult rv = aEditorBase.DeleteSelectionAsAction(
    619      deleteDir, nsIEditor::eStrip, aPrincipal);
    620  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    621                       "EditorBase::DeleteSelectionAsAction() failed");
    622  return rv;
    623 }
    624 
    625 nsresult DeleteCommand::GetCommandStateParams(
    626    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    627    nsIEditingSession* aEditingSession) const {
    628  return aParams.SetBool(STATE_ENABLED,
    629                         IsCommandEnabled(aCommand, aEditorBase));
    630 }
    631 
    632 /******************************************************************************
    633 * mozilla::SelectAllCommand
    634 ******************************************************************************/
    635 
    636 StaticRefPtr<SelectAllCommand> SelectAllCommand::sInstance;
    637 
    638 bool SelectAllCommand::IsCommandEnabled(Command aCommand,
    639                                        EditorBase* aEditorBase) const {
    640  // You can always select all, unless the selection is editable,
    641  // and the editable region is empty!
    642  if (!aEditorBase) {
    643    return true;
    644  }
    645 
    646  // You can select all if there is an editor which is non-empty
    647  return !aEditorBase->IsEmpty();
    648 }
    649 
    650 nsresult SelectAllCommand::DoCommand(Command aCommand, EditorBase& aEditorBase,
    651                                     nsIPrincipal* aPrincipal) const {
    652  // Shouldn't cause "beforeinput" event so that we don't need to specify
    653  // aPrincipal.
    654  nsresult rv = aEditorBase.SelectAll();
    655  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::SelectAll() failed");
    656  return rv;
    657 }
    658 
    659 nsresult SelectAllCommand::GetCommandStateParams(
    660    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    661    nsIEditingSession* aEditingSession) const {
    662  return aParams.SetBool(STATE_ENABLED,
    663                         IsCommandEnabled(aCommand, aEditorBase));
    664 }
    665 
    666 /******************************************************************************
    667 * mozilla::SelectionMoveCommands
    668 ******************************************************************************/
    669 
    670 StaticRefPtr<SelectionMoveCommands> SelectionMoveCommands::sInstance;
    671 
    672 bool SelectionMoveCommands::IsCommandEnabled(Command aCommand,
    673                                             EditorBase* aEditorBase) const {
    674  if (!aEditorBase) {
    675    return false;
    676  }
    677  return aEditorBase->IsSelectionEditable();
    678 }
    679 
    680 static const struct ScrollCommand {
    681  Command mReverseScroll;
    682  Command mForwardScroll;
    683  nsresult (NS_STDCALL nsISelectionController::*scroll)(bool);
    684 } scrollCommands[] = {{Command::ScrollTop, Command::ScrollBottom,
    685                       &nsISelectionController::CompleteScroll},
    686                      {Command::ScrollPageUp, Command::ScrollPageDown,
    687                       &nsISelectionController::ScrollPage},
    688                      {Command::ScrollLineUp, Command::ScrollLineDown,
    689                       &nsISelectionController::ScrollLine}};
    690 
    691 static const struct MoveCommand {
    692  Command mReverseMove;
    693  Command mForwardMove;
    694  Command mReverseSelect;
    695  Command mForwardSelect;
    696  nsresult (NS_STDCALL nsISelectionController::*move)(bool, bool);
    697 } moveCommands[] = {
    698    {Command::CharPrevious, Command::CharNext, Command::SelectCharPrevious,
    699     Command::SelectCharNext, &nsISelectionController::CharacterMove},
    700    {Command::LinePrevious, Command::LineNext, Command::SelectLinePrevious,
    701     Command::SelectLineNext, &nsISelectionController::LineMove},
    702    {Command::WordPrevious, Command::WordNext, Command::SelectWordPrevious,
    703     Command::SelectWordNext, &nsISelectionController::WordMove},
    704    {Command::BeginLine, Command::EndLine, Command::SelectBeginLine,
    705     Command::SelectEndLine, &nsISelectionController::IntraLineMove},
    706    {Command::MovePageUp, Command::MovePageDown, Command::SelectPageUp,
    707     Command::SelectPageDown, &nsISelectionController::PageMove},
    708    {Command::MoveTop, Command::MoveBottom, Command::SelectTop,
    709     Command::SelectBottom, &nsISelectionController::CompleteMove}};
    710 
    711 static const struct PhysicalCommand {
    712  Command mMove;
    713  Command mSelect;
    714  int16_t direction;
    715  int16_t amount;
    716 } physicalCommands[] = {
    717    {Command::MoveLeft, Command::SelectLeft, nsISelectionController::MOVE_LEFT,
    718     0},
    719    {Command::MoveRight, Command::SelectRight,
    720     nsISelectionController::MOVE_RIGHT, 0},
    721    {Command::MoveUp, Command::SelectUp, nsISelectionController::MOVE_UP, 0},
    722    {Command::MoveDown, Command::SelectDown, nsISelectionController::MOVE_DOWN,
    723     0},
    724    {Command::MoveLeft2, Command::SelectLeft2,
    725     nsISelectionController::MOVE_LEFT, 1},
    726    {Command::MoveRight2, Command::SelectRight2,
    727     nsISelectionController::MOVE_RIGHT, 1},
    728    {Command::MoveUp2, Command::SelectUp2, nsISelectionController::MOVE_UP, 1},
    729    {Command::MoveDown2, Command::SelectDown2,
    730     nsISelectionController::MOVE_DOWN, 1}};
    731 
    732 nsresult SelectionMoveCommands::DoCommand(Command aCommand,
    733                                          EditorBase& aEditorBase,
    734                                          nsIPrincipal* aPrincipal) const {
    735  RefPtr<dom::Document> document = aEditorBase.GetDocument();
    736  if (document) {
    737    // Most of the commands below (possibly all of them) need layout to
    738    // be up to date.
    739    document->FlushPendingNotifications(FlushType::Layout);
    740  }
    741 
    742  nsCOMPtr<nsISelectionController> selectionController =
    743      aEditorBase.GetSelectionController();
    744  if (NS_WARN_IF(!selectionController)) {
    745    return NS_ERROR_FAILURE;
    746  }
    747 
    748  // scroll commands
    749  for (size_t i = 0; i < std::size(scrollCommands); i++) {
    750    const ScrollCommand& cmd = scrollCommands[i];
    751    if (aCommand == cmd.mReverseScroll) {
    752      return (selectionController->*(cmd.scroll))(false);
    753    }
    754    if (aCommand == cmd.mForwardScroll) {
    755      return (selectionController->*(cmd.scroll))(true);
    756    }
    757  }
    758 
    759  // caret movement/selection commands
    760  for (size_t i = 0; i < std::size(moveCommands); i++) {
    761    const MoveCommand& cmd = moveCommands[i];
    762    if (aCommand == cmd.mReverseMove) {
    763      return (selectionController->*(cmd.move))(false, false);
    764    }
    765    if (aCommand == cmd.mForwardMove) {
    766      return (selectionController->*(cmd.move))(true, false);
    767    }
    768    if (aCommand == cmd.mReverseSelect) {
    769      return (selectionController->*(cmd.move))(false, true);
    770    }
    771    if (aCommand == cmd.mForwardSelect) {
    772      return (selectionController->*(cmd.move))(true, true);
    773    }
    774  }
    775 
    776  // physical-direction movement/selection
    777  for (size_t i = 0; i < std::size(physicalCommands); i++) {
    778    const PhysicalCommand& cmd = physicalCommands[i];
    779    if (aCommand == cmd.mMove) {
    780      nsresult rv =
    781          selectionController->PhysicalMove(cmd.direction, cmd.amount, false);
    782      NS_WARNING_ASSERTION(
    783          NS_SUCCEEDED(rv),
    784          "nsISelectionController::PhysicalMove() failed to move caret");
    785      return rv;
    786    }
    787    if (aCommand == cmd.mSelect) {
    788      nsresult rv =
    789          selectionController->PhysicalMove(cmd.direction, cmd.amount, true);
    790      NS_WARNING_ASSERTION(
    791          NS_SUCCEEDED(rv),
    792          "nsISelectionController::PhysicalMove() failed to select");
    793      return rv;
    794    }
    795  }
    796 
    797  return NS_ERROR_FAILURE;
    798 }
    799 
    800 nsresult SelectionMoveCommands::GetCommandStateParams(
    801    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    802    nsIEditingSession* aEditingSession) const {
    803  return aParams.SetBool(STATE_ENABLED,
    804                         IsCommandEnabled(aCommand, aEditorBase));
    805 }
    806 
    807 /******************************************************************************
    808 * mozilla::InsertPlaintextCommand
    809 ******************************************************************************/
    810 
    811 StaticRefPtr<InsertPlaintextCommand> InsertPlaintextCommand::sInstance;
    812 
    813 bool InsertPlaintextCommand::IsCommandEnabled(Command aCommand,
    814                                              EditorBase* aEditorBase) const {
    815  if (!aEditorBase) {
    816    return false;
    817  }
    818  return aEditorBase->IsModifiable() && aEditorBase->IsSelectionEditable();
    819 }
    820 
    821 nsresult InsertPlaintextCommand::DoCommand(Command aCommand,
    822                                           EditorBase& aEditorBase,
    823                                           nsIPrincipal* aPrincipal) const {
    824  // XXX InsertTextAsAction() is not same as OnInputText().  However, other
    825  //     commands to insert line break or paragraph separator use OnInput*().
    826  //     According to the semantics of those methods, using *AsAction() is
    827  //     better, however, this may not cause two or more placeholder
    828  //     transactions to the top transaction since its name may not be
    829  //     nsGkAtoms::TypingTxnName.
    830  DebugOnly<nsresult> rvIgnored =
    831      aEditorBase.InsertTextAsAction(u""_ns, aPrincipal);
    832  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    833                       "EditorBase::InsertTextAsAction() failed, but ignored");
    834  return NS_OK;
    835 }
    836 
    837 nsresult InsertPlaintextCommand::DoCommandParam(
    838    Command aCommand, const nsAString& aStringParam, EditorBase& aEditorBase,
    839    nsIPrincipal* aPrincipal) const {
    840  if (NS_WARN_IF(aStringParam.IsVoid())) {
    841    return NS_ERROR_INVALID_ARG;
    842  }
    843 
    844  // XXX InsertTextAsAction() is not same as OnInputText().  However, other
    845  //     commands to insert line break or paragraph separator use OnInput*().
    846  //     According to the semantics of those methods, using *AsAction() is
    847  //     better, however, this may not cause two or more placeholder
    848  //     transactions to the top transaction since its name may not be
    849  //     nsGkAtoms::TypingTxnName.
    850  DebugOnly<nsresult> rvIgnored =
    851      aEditorBase.InsertTextAsAction(aStringParam, aPrincipal);
    852  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
    853                       "EditorBase::InsertTextAsAction() failed, but ignored");
    854  return NS_OK;
    855 }
    856 
    857 nsresult InsertPlaintextCommand::GetCommandStateParams(
    858    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    859    nsIEditingSession* aEditingSession) const {
    860  return aParams.SetBool(STATE_ENABLED,
    861                         IsCommandEnabled(aCommand, aEditorBase));
    862 }
    863 
    864 /******************************************************************************
    865 * mozilla::InsertParagraphCommand
    866 ******************************************************************************/
    867 
    868 StaticRefPtr<InsertParagraphCommand> InsertParagraphCommand::sInstance;
    869 
    870 bool InsertParagraphCommand::IsCommandEnabled(Command aCommand,
    871                                              EditorBase* aEditorBase) const {
    872  if (!aEditorBase || aEditorBase->IsSingleLineEditor()) {
    873    return false;
    874  }
    875  return aEditorBase->IsModifiable() && aEditorBase->IsSelectionEditable();
    876 }
    877 
    878 nsresult InsertParagraphCommand::DoCommand(Command aCommand,
    879                                           EditorBase& aEditorBase,
    880                                           nsIPrincipal* aPrincipal) const {
    881  if (aEditorBase.IsSingleLineEditor()) {
    882    return NS_ERROR_FAILURE;
    883  }
    884  if (aEditorBase.IsHTMLEditor()) {
    885    nsresult rv = MOZ_KnownLive(aEditorBase.AsHTMLEditor())
    886                      ->InsertParagraphSeparatorAsAction(aPrincipal);
    887    NS_WARNING_ASSERTION(
    888        NS_SUCCEEDED(rv),
    889        "HTMLEditor::InsertParagraphSeparatorAsAction() failed");
    890    return rv;
    891  }
    892  nsresult rv = aEditorBase.InsertLineBreakAsAction(aPrincipal);
    893  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    894                       "EditorBase::InsertLineBreakAsAction() failed");
    895  return rv;
    896 }
    897 
    898 nsresult InsertParagraphCommand::GetCommandStateParams(
    899    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    900    nsIEditingSession* aEditingSession) const {
    901  return aParams.SetBool(STATE_ENABLED,
    902                         IsCommandEnabled(aCommand, aEditorBase));
    903 }
    904 
    905 /******************************************************************************
    906 * mozilla::InsertLineBreakCommand
    907 ******************************************************************************/
    908 
    909 StaticRefPtr<InsertLineBreakCommand> InsertLineBreakCommand::sInstance;
    910 
    911 bool InsertLineBreakCommand::IsCommandEnabled(Command aCommand,
    912                                              EditorBase* aEditorBase) const {
    913  if (!aEditorBase || aEditorBase->IsSingleLineEditor()) {
    914    return false;
    915  }
    916  return aEditorBase->IsModifiable() && aEditorBase->IsSelectionEditable();
    917 }
    918 
    919 nsresult InsertLineBreakCommand::DoCommand(Command aCommand,
    920                                           EditorBase& aEditorBase,
    921                                           nsIPrincipal* aPrincipal) const {
    922  if (aEditorBase.IsSingleLineEditor()) {
    923    return NS_ERROR_FAILURE;
    924  }
    925  nsresult rv = aEditorBase.InsertLineBreakAsAction(aPrincipal);
    926  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    927                       "EditorBase::InsertLineBreakAsAction() failed");
    928  return rv;
    929 }
    930 
    931 nsresult InsertLineBreakCommand::GetCommandStateParams(
    932    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    933    nsIEditingSession* aEditingSession) const {
    934  return aParams.SetBool(STATE_ENABLED,
    935                         IsCommandEnabled(aCommand, aEditorBase));
    936 }
    937 
    938 /******************************************************************************
    939 * mozilla::PasteQuotationCommand
    940 ******************************************************************************/
    941 
    942 StaticRefPtr<PasteQuotationCommand> PasteQuotationCommand::sInstance;
    943 
    944 bool PasteQuotationCommand::IsCommandEnabled(Command aCommand,
    945                                             EditorBase* aEditorBase) const {
    946  if (!aEditorBase) {
    947    return false;
    948  }
    949  return !aEditorBase->IsSingleLineEditor() &&
    950         aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard);
    951 }
    952 
    953 nsresult PasteQuotationCommand::DoCommand(Command aCommand,
    954                                          EditorBase& aEditorBase,
    955                                          nsIPrincipal* aPrincipal) const {
    956  nsresult rv = aEditorBase.PasteAsQuotationAsAction(
    957      nsIClipboard::kGlobalClipboard, EditorBase::DispatchPasteEvent::Yes,
    958      nullptr, aPrincipal);
    959  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    960                       "EditorBase::PasteAsQuotationAsAction(nsIClipboard::"
    961                       "kGlobalClipboard, DispatchPasteEvent::Yes) failed");
    962  return rv;
    963 }
    964 
    965 nsresult PasteQuotationCommand::GetCommandStateParams(
    966    Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase,
    967    nsIEditingSession* aEditingSession) const {
    968  if (!aEditorBase) {
    969    return NS_OK;
    970  }
    971  aParams.SetBool(STATE_ENABLED,
    972                  aEditorBase->CanPaste(nsIClipboard::kGlobalClipboard));
    973  return NS_OK;
    974 }
    975 
    976 }  // namespace mozilla