tor-browser

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

ReplaceTextTransaction.cpp (9841B)


      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 "ReplaceTextTransaction.h"
      7 
      8 #include "HTMLEditUtils.h"
      9 
     10 #include "mozilla/Logging.h"
     11 #include "mozilla/OwningNonNull.h"
     12 #include "mozilla/TextEditor.h"
     13 #include "mozilla/ToString.h"
     14 
     15 namespace mozilla {
     16 
     17 using namespace dom;
     18 
     19 // static
     20 already_AddRefed<ReplaceTextTransaction> ReplaceTextTransaction::Create(
     21    EditorBase& aEditorBase, const nsAString& aStringToInsert,
     22    dom::Text& aTextNode, uint32_t aStartOffset, uint32_t aLength) {
     23  MOZ_ASSERT(aLength > 0, "Use InsertTextTransaction instead");
     24  MOZ_ASSERT(!aStringToInsert.IsEmpty(), "Use DeleteTextTransaction instead");
     25  MOZ_ASSERT(aTextNode.Length() >= aStartOffset);
     26  MOZ_ASSERT(aTextNode.Length() >= aStartOffset + aLength);
     27 
     28  RefPtr<ReplaceTextTransaction> transaction =
     29      aEditorBase.IsTextEditor()
     30          ? new ReplaceTextTransaction(aEditorBase, aStringToInsert, aTextNode,
     31                                       aStartOffset, aLength)
     32          : new ReplaceTextInTextNodeTransaction(
     33                aEditorBase, aStringToInsert, aTextNode, aStartOffset, aLength);
     34  return transaction.forget();
     35 }
     36 
     37 std::ostream& operator<<(std::ostream& aStream,
     38                         const ReplaceTextTransaction& aTransaction) {
     39  const auto* transactionForHTMLEditor =
     40      aTransaction.GetAsReplaceTextInTextNodeTransaction();
     41  if (transactionForHTMLEditor) {
     42    return aStream << *transactionForHTMLEditor;
     43  }
     44  aStream << "{ mStringToInsert=\""
     45          << NS_ConvertUTF16toUTF8(aTransaction.mStringToInsert).get() << "\""
     46          << ", mStringToBeReplaced=\""
     47          << NS_ConvertUTF16toUTF8(aTransaction.mStringToBeReplaced).get()
     48          << "\", mOffset=" << aTransaction.mOffset
     49          << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
     50  return aStream;
     51 }
     52 
     53 NS_IMPL_CYCLE_COLLECTION_INHERITED(ReplaceTextTransaction, EditTransactionBase,
     54                                   mEditorBase)
     55 
     56 NS_IMPL_ADDREF_INHERITED(ReplaceTextTransaction, EditTransactionBase)
     57 NS_IMPL_RELEASE_INHERITED(ReplaceTextTransaction, EditTransactionBase)
     58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReplaceTextTransaction)
     59 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
     60 
     61 Text* ReplaceTextTransaction::GetTextNode() const {
     62  if (MOZ_UNLIKELY(!mEditorBase)) {
     63    return nullptr;
     64  }
     65  if (TextEditor* const textEditor = mEditorBase->GetAsTextEditor()) {
     66    return textEditor->GetTextNode();
     67  }
     68  MOZ_ASSERT(GetAsReplaceTextInTextNodeTransaction());
     69  return GetAsReplaceTextInTextNodeTransaction()->mTextNode;
     70 }
     71 
     72 NS_IMETHODIMP ReplaceTextTransaction::DoTransaction() {
     73  MOZ_LOG(GetLogModule(), LogLevel::Info,
     74          ("%p ReplaceTextTransaction::%s this=%s", this, __FUNCTION__,
     75           ToString(*this).c_str()));
     76 
     77  if (NS_WARN_IF(!mEditorBase)) {
     78    return NS_ERROR_NOT_AVAILABLE;
     79  }
     80  const RefPtr<Text> textNode = GetTextNode();
     81  if (NS_WARN_IF(!textNode) ||
     82      (mEditorBase->IsHTMLEditor() &&
     83       NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) {
     84    return NS_ERROR_NOT_AVAILABLE;
     85  }
     86 
     87  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
     88 
     89  IgnoredErrorResult error;
     90  editorBase->DoReplaceText(*textNode, mOffset, mStringToBeReplaced.Length(),
     91                            mStringToInsert, error);
     92  if (MOZ_UNLIKELY(error.Failed())) {
     93    NS_WARNING("EditorBase::DoReplaceText() failed");
     94    return error.StealNSResult();
     95  }
     96  // XXX What should we do if mutation event listener changed the node?
     97  editorBase->RangeUpdaterRef().SelAdjReplaceText(*textNode, mOffset,
     98                                                  mStringToBeReplaced.Length(),
     99                                                  mStringToInsert.Length());
    100  return NS_OK;
    101 }
    102 
    103 NS_IMETHODIMP ReplaceTextTransaction::UndoTransaction() {
    104  MOZ_LOG(GetLogModule(), LogLevel::Info,
    105          ("%p ReplaceTextTransaction::%s this=%s", this, __FUNCTION__,
    106           ToString(*this).c_str()));
    107 
    108  if (NS_WARN_IF(!mEditorBase)) {
    109    return NS_ERROR_NOT_AVAILABLE;
    110  }
    111  const RefPtr<Text> textNode = GetTextNode();
    112  if (NS_WARN_IF(!textNode) ||
    113      (mEditorBase->IsHTMLEditor() &&
    114       NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) {
    115    return NS_ERROR_NOT_AVAILABLE;
    116  }
    117 
    118  IgnoredErrorResult error;
    119  nsAutoString insertedString;
    120  textNode->SubstringData(mOffset, mStringToInsert.Length(), insertedString,
    121                          error);
    122  if (MOZ_UNLIKELY(error.Failed())) {
    123    NS_WARNING("CharacterData::SubstringData() failed");
    124    return error.StealNSResult();
    125  }
    126  if (MOZ_UNLIKELY(insertedString != mStringToInsert)) {
    127    NS_WARNING(
    128        "ReplaceTextTransaction::UndoTransaction() did nothing due to "
    129        "unexpected text");
    130    return NS_OK;
    131  }
    132 
    133  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    134 
    135  editorBase->DoReplaceText(*textNode, mOffset, mStringToInsert.Length(),
    136                            mStringToBeReplaced, error);
    137  if (MOZ_UNLIKELY(error.Failed())) {
    138    NS_WARNING("EditorBase::DoReplaceText() failed");
    139    return error.StealNSResult();
    140  }
    141  // XXX What should we do if mutation event listener changed the node?
    142  editorBase->RangeUpdaterRef().SelAdjReplaceText(*textNode, mOffset,
    143                                                  mStringToInsert.Length(),
    144                                                  mStringToBeReplaced.Length());
    145 
    146  if (!editorBase->AllowsTransactionsToChangeSelection()) {
    147    return NS_OK;
    148  }
    149 
    150  // XXX Should we stop setting selection when mutation event listener
    151  //     modifies the text node?
    152  editorBase->CollapseSelectionTo(
    153      EditorRawDOMPoint(textNode, mOffset + mStringToBeReplaced.Length()),
    154      error);
    155  if (MOZ_UNLIKELY(error.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
    156    NS_WARNING(
    157        "EditorBase::CollapseSelectionTo() caused destroying the editor");
    158    return NS_ERROR_EDITOR_DESTROYED;
    159  }
    160  NS_ASSERTION(!error.Failed(),
    161               "EditorBase::CollapseSelectionTo() failed, but ignored");
    162  return NS_OK;
    163 }
    164 
    165 NS_IMETHODIMP ReplaceTextTransaction::RedoTransaction() {
    166  MOZ_LOG(GetLogModule(), LogLevel::Info,
    167          ("%p ReplaceTextTransaction::%s this=%s", this, __FUNCTION__,
    168           ToString(*this).c_str()));
    169 
    170  if (NS_WARN_IF(!mEditorBase)) {
    171    return NS_ERROR_NOT_AVAILABLE;
    172  }
    173  const RefPtr<Text> textNode = GetTextNode();
    174  if (NS_WARN_IF(!textNode) ||
    175      (mEditorBase->IsHTMLEditor() &&
    176       NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(*textNode)))) {
    177    return NS_ERROR_NOT_AVAILABLE;
    178  }
    179 
    180  IgnoredErrorResult error;
    181  nsAutoString undoneString;
    182  textNode->SubstringData(mOffset, mStringToBeReplaced.Length(), undoneString,
    183                          error);
    184  if (MOZ_UNLIKELY(error.Failed())) {
    185    NS_WARNING("CharacterData::SubstringData() failed");
    186    return error.StealNSResult();
    187  }
    188  if (MOZ_UNLIKELY(undoneString != mStringToBeReplaced)) {
    189    NS_WARNING(
    190        "ReplaceTextTransaction::RedoTransaction() did nothing due to "
    191        "unexpected text");
    192    return NS_OK;
    193  }
    194 
    195  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    196  editorBase->DoReplaceText(*textNode, mOffset, mStringToBeReplaced.Length(),
    197                            mStringToInsert, error);
    198  if (MOZ_UNLIKELY(error.Failed())) {
    199    NS_WARNING("EditorBase::DoReplaceText() failed");
    200    return error.StealNSResult();
    201  }
    202  // XXX What should we do if mutation event listener changed the node?
    203  editorBase->RangeUpdaterRef().SelAdjReplaceText(*textNode, mOffset,
    204                                                  mStringToBeReplaced.Length(),
    205                                                  mStringToInsert.Length());
    206 
    207  if (!editorBase->AllowsTransactionsToChangeSelection()) {
    208    return NS_OK;
    209  }
    210 
    211  // XXX Should we stop setting selection when mutation event listener
    212  //     modifies the text node?
    213  editorBase->CollapseSelectionTo(SuggestPointToPutCaret<EditorRawDOMPoint>(),
    214                                  error);
    215  if (MOZ_UNLIKELY(error.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
    216    NS_WARNING(
    217        "EditorBase::CollapseSelectionTo() caused destroying the editor");
    218    return NS_ERROR_EDITOR_DESTROYED;
    219  }
    220  NS_ASSERTION(!error.Failed(),
    221               "EditorBase::CollapseSelectionTo() failed, but ignored");
    222  return NS_OK;
    223 }
    224 
    225 /******************************************************************************
    226 *
    227 ******************************************************************************/
    228 
    229 std::ostream& operator<<(std::ostream& aStream,
    230                         const ReplaceTextInTextNodeTransaction& aTransaction) {
    231  aStream << "{ mTextNode=" << aTransaction.mTextNode.get();
    232  if (aTransaction.mTextNode) {
    233    aStream << " (" << *aTransaction.mTextNode << ")";
    234  }
    235  aStream << ", mStringToInsert=\""
    236          << NS_ConvertUTF16toUTF8(aTransaction.mStringToInsert).get() << "\""
    237          << ", mStringToBeReplaced=\""
    238          << NS_ConvertUTF16toUTF8(aTransaction.mStringToBeReplaced).get()
    239          << "\", mOffset=" << aTransaction.mOffset
    240          << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
    241  return aStream;
    242 }
    243 
    244 NS_IMPL_CYCLE_COLLECTION_INHERITED(ReplaceTextInTextNodeTransaction,
    245                                   ReplaceTextTransaction, mTextNode)
    246 
    247 NS_IMPL_ADDREF_INHERITED(ReplaceTextInTextNodeTransaction,
    248                         ReplaceTextTransaction)
    249 NS_IMPL_RELEASE_INHERITED(ReplaceTextInTextNodeTransaction,
    250                          ReplaceTextTransaction)
    251 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReplaceTextInTextNodeTransaction)
    252 NS_INTERFACE_MAP_END_INHERITING(ReplaceTextTransaction)
    253 
    254 }  // namespace mozilla