tor-browser

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

DeleteTextTransaction.cpp (9392B)


      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 "DeleteTextTransaction.h"
      7 
      8 #include "EditorBase.h"
      9 #include "EditorDOMPoint.h"
     10 #include "HTMLEditUtils.h"
     11 #include "SelectionState.h"
     12 
     13 #include "mozilla/Assertions.h"
     14 #include "mozilla/TextEditor.h"
     15 #include "mozilla/dom/Selection.h"
     16 
     17 #include "nsDebug.h"
     18 #include "nsError.h"
     19 #include "nsISupportsImpl.h"
     20 #include "nsAString.h"
     21 
     22 namespace mozilla {
     23 
     24 using namespace dom;
     25 
     26 // static
     27 already_AddRefed<DeleteTextTransaction> DeleteTextTransaction::MaybeCreate(
     28    EditorBase& aEditorBase, Text& aTextNode, uint32_t aOffset,
     29    uint32_t aLengthToDelete) {
     30  RefPtr<DeleteTextTransaction> transaction =
     31      aEditorBase.IsTextEditor()
     32          ? new DeleteTextTransaction(aEditorBase, aTextNode, aOffset,
     33                                      aLengthToDelete)
     34          : new DeleteTextFromTextNodeTransaction(aEditorBase, aTextNode,
     35                                                  aOffset, aLengthToDelete);
     36  return transaction.forget();
     37 }
     38 
     39 // static
     40 already_AddRefed<DeleteTextTransaction>
     41 DeleteTextTransaction::MaybeCreateForPreviousCharacter(EditorBase& aEditorBase,
     42                                                       Text& aTextNode,
     43                                                       uint32_t aOffset) {
     44  if (NS_WARN_IF(!aOffset)) {
     45    return nullptr;
     46  }
     47 
     48  nsAutoString data;
     49  aTextNode.GetData(data);
     50  if (NS_WARN_IF(data.IsEmpty())) {
     51    return nullptr;
     52  }
     53 
     54  uint32_t length = 1;
     55  uint32_t offset = aOffset - 1;
     56  if (offset && NS_IS_SURROGATE_PAIR(data[offset - 1], data[offset])) {
     57    ++length;
     58    --offset;
     59  }
     60  return DeleteTextTransaction::MaybeCreate(aEditorBase, aTextNode, offset,
     61                                            length);
     62 }
     63 
     64 // static
     65 already_AddRefed<DeleteTextTransaction>
     66 DeleteTextTransaction::MaybeCreateForNextCharacter(EditorBase& aEditorBase,
     67                                                   Text& aTextNode,
     68                                                   uint32_t aOffset) {
     69  nsAutoString data;
     70  aTextNode.GetData(data);
     71  if (NS_WARN_IF(aOffset >= data.Length()) || NS_WARN_IF(data.IsEmpty())) {
     72    return nullptr;
     73  }
     74 
     75  uint32_t length = 1;
     76  if (aOffset + 1 < data.Length() &&
     77      NS_IS_SURROGATE_PAIR(data[aOffset], data[aOffset + 1])) {
     78    ++length;
     79  }
     80  return DeleteTextTransaction::MaybeCreate(aEditorBase, aTextNode, aOffset,
     81                                            length);
     82 }
     83 
     84 DeleteTextTransaction::DeleteTextTransaction(EditorBase& aEditorBase,
     85                                             Text& aTextNode, uint32_t aOffset,
     86                                             uint32_t aLengthToDelete)
     87    : DeleteContentTransactionBase(aEditorBase),
     88      mOffset(aOffset),
     89      mLengthToDelete(aLengthToDelete) {
     90  MOZ_ASSERT(aTextNode.TextDataLength() >= aOffset + aLengthToDelete);
     91 }
     92 
     93 std::ostream& operator<<(std::ostream& aStream,
     94                         const DeleteTextTransaction& aTransaction) {
     95  const auto* transactionForHTMLEditor =
     96      aTransaction.GetAsDeleteTextFromTextNodeTransaction();
     97  if (transactionForHTMLEditor) {
     98    return aStream << *transactionForHTMLEditor;
     99  }
    100  aStream << "{ mOffset=" << aTransaction.mOffset
    101          << ", mLengthToDelete=" << aTransaction.mLengthToDelete
    102          << ", mDeletedText=\""
    103          << NS_ConvertUTF16toUTF8(aTransaction.mDeletedText).get() << "\""
    104          << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
    105  return aStream;
    106 }
    107 
    108 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTransaction,
    109                                   DeleteContentTransactionBase)
    110 
    111 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTransaction)
    112 NS_INTERFACE_MAP_END_INHERITING(DeleteContentTransactionBase)
    113 
    114 Text* DeleteTextTransaction::GetTextNode() const {
    115  if (MOZ_UNLIKELY(!mEditorBase)) {
    116    return nullptr;
    117  }
    118  if (TextEditor* const textEditor = mEditorBase->GetAsTextEditor()) {
    119    return textEditor->GetTextNode();
    120  }
    121  MOZ_ASSERT(GetAsDeleteTextFromTextNodeTransaction());
    122  return GetAsDeleteTextFromTextNodeTransaction()->mTextNode;
    123 }
    124 
    125 NS_IMETHODIMP DeleteTextTransaction::DoTransaction() {
    126  MOZ_LOG(GetLogModule(), LogLevel::Info,
    127          ("%p DeleteTextTransaction::%s this=%s", this, __FUNCTION__,
    128           ToString(*this).c_str()));
    129 
    130  if (NS_WARN_IF(!mEditorBase)) {
    131    return NS_ERROR_NOT_AVAILABLE;
    132  }
    133  const RefPtr<Text> textNode = GetTextNode();
    134  if (NS_WARN_IF(!textNode) ||
    135      (mEditorBase->IsHTMLEditor() &&
    136       NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) {
    137    return NS_ERROR_NOT_AVAILABLE;
    138  }
    139 
    140  // Get the text that we're about to delete
    141  IgnoredErrorResult error;
    142  textNode->SubstringData(mOffset, mLengthToDelete, mDeletedText, error);
    143  if (MOZ_UNLIKELY(error.Failed())) {
    144    NS_WARNING("Text::SubstringData() failed");
    145    return error.StealNSResult();
    146  }
    147 
    148  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    149  editorBase->DoDeleteText(*textNode, mOffset, mLengthToDelete, error);
    150  if (MOZ_UNLIKELY(error.Failed())) {
    151    NS_WARNING("EditorBase::DoDeleteText() failed");
    152    return error.StealNSResult();
    153  }
    154 
    155  editorBase->RangeUpdaterRef().SelAdjDeleteText(*textNode, mOffset,
    156                                                 mLengthToDelete);
    157  return NS_OK;
    158 }
    159 
    160 EditorDOMPoint DeleteTextTransaction::SuggestPointToPutCaret() const {
    161  if (NS_WARN_IF(!mEditorBase)) {
    162    return EditorDOMPoint();
    163  }
    164  Text* const textNode = GetTextNode();
    165  if (NS_WARN_IF(!textNode) ||
    166      (mEditorBase->IsHTMLEditor() &&
    167       NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) {
    168    return EditorDOMPoint();
    169  }
    170  if (NS_WARN_IF(textNode->TextDataLength() < mOffset)) {
    171    return EditorDOMPoint();
    172  }
    173  EditorDOMPoint candidatePoint(textNode, mOffset);
    174  if (!candidatePoint.IsInNativeAnonymousSubtreeInTextControl() &&
    175      !HTMLEditUtils::IsSimplyEditableNode(*textNode)) {
    176    return EditorDOMPoint();
    177  }
    178  return candidatePoint;
    179 }
    180 
    181 // XXX: We may want to store the selection state and restore it properly.  Was
    182 //     it an insertion point or an extended selection?
    183 NS_IMETHODIMP DeleteTextTransaction::UndoTransaction() {
    184  MOZ_LOG(GetLogModule(), LogLevel::Info,
    185          ("%p DeleteTextTransaction::%s this=%s", this, __FUNCTION__,
    186           ToString(*this).c_str()));
    187 
    188  if (NS_WARN_IF(!mEditorBase)) {
    189    return NS_ERROR_NOT_AVAILABLE;
    190  }
    191  const RefPtr<Text> textNode = GetTextNode();
    192  if (NS_WARN_IF(!textNode) ||
    193      (mEditorBase->IsHTMLEditor() &&
    194       NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) {
    195    return NS_ERROR_NOT_AVAILABLE;
    196  }
    197  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    198  IgnoredErrorResult error;
    199  editorBase->DoInsertText(*textNode, mOffset, mDeletedText, error);
    200  NS_WARNING_ASSERTION(!error.Failed(), "EditorBase::DoInsertText() failed");
    201  return error.StealNSResult();
    202 }
    203 
    204 NS_IMETHODIMP DeleteTextTransaction::RedoTransaction() {
    205  MOZ_LOG(GetLogModule(), LogLevel::Info,
    206          ("%p DeleteTextTransaction::%s this=%s", this, __FUNCTION__,
    207           !mEditorBase || mEditorBase->IsTextEditor()
    208               ? ToString(*this).c_str()
    209               : ToString(*GetAsDeleteTextFromTextNodeTransaction()).c_str()));
    210  nsresult rv = DoTransaction();
    211  if (NS_FAILED(rv)) {
    212    NS_WARNING("DeleteTextTransaction::DoTransaction() failed");
    213    return rv;
    214  }
    215  if (!mEditorBase || !mEditorBase->AllowsTransactionsToChangeSelection()) {
    216    return NS_OK;
    217  }
    218  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    219  rv = editorBase->CollapseSelectionTo(SuggestPointToPutCaret());
    220  if (NS_FAILED(rv)) {
    221    NS_WARNING("EditorBase::CollapseSelectionTo() failed");
    222    return rv;
    223  }
    224  return NS_OK;
    225 }
    226 
    227 /******************************************************************************
    228 * mozilla::DeleteTextFromTextNodeTransaction
    229 ******************************************************************************/
    230 
    231 DeleteTextFromTextNodeTransaction::DeleteTextFromTextNodeTransaction(
    232    EditorBase& aEditorBase, Text& aTextNode, uint32_t aOffset,
    233    uint32_t aLengthToDelete)
    234    : DeleteTextTransaction(aEditorBase, aTextNode, aOffset, aLengthToDelete),
    235      mTextNode(&aTextNode) {
    236  MOZ_ASSERT(aEditorBase.IsHTMLEditor());
    237  MOZ_ASSERT(mTextNode->TextDataLength() >= aOffset + aLengthToDelete);
    238 }
    239 
    240 std::ostream& operator<<(
    241    std::ostream& aStream,
    242    const DeleteTextFromTextNodeTransaction& aTransaction) {
    243  aStream << "{ mTextNode=" << aTransaction.mTextNode.get();
    244  if (aTransaction.mTextNode) {
    245    aStream << " (" << *aTransaction.mTextNode << ")";
    246  }
    247  aStream << ", mOffset=" << aTransaction.mOffset
    248          << ", mLengthToDelete=" << aTransaction.mLengthToDelete
    249          << ", mDeletedText=\""
    250          << NS_ConvertUTF16toUTF8(aTransaction.mDeletedText).get() << "\""
    251          << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
    252  return aStream;
    253 }
    254 
    255 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextFromTextNodeTransaction,
    256                                   DeleteTextTransaction, mTextNode)
    257 
    258 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextFromTextNodeTransaction)
    259 NS_INTERFACE_MAP_END_INHERITING(DeleteTextTransaction)
    260 
    261 }  // namespace mozilla